diff --git a/chatgpt-web/Makefile b/luci-app-chatgpt-web/Makefile similarity index 100% rename from chatgpt-web/Makefile rename to luci-app-chatgpt-web/Makefile diff --git a/chatgpt-web/README.md b/luci-app-chatgpt-web/README.md similarity index 100% rename from chatgpt-web/README.md rename to luci-app-chatgpt-web/README.md diff --git a/chatgpt-web/luasrc/controller/chatgpt-web.lua b/luci-app-chatgpt-web/luasrc/controller/chatgpt-web.lua similarity index 100% rename from chatgpt-web/luasrc/controller/chatgpt-web.lua rename to luci-app-chatgpt-web/luasrc/controller/chatgpt-web.lua diff --git a/chatgpt-web/luasrc/model/cbi/chatgpt-web.lua b/luci-app-chatgpt-web/luasrc/model/cbi/chatgpt-web.lua similarity index 100% rename from chatgpt-web/luasrc/model/cbi/chatgpt-web.lua rename to luci-app-chatgpt-web/luasrc/model/cbi/chatgpt-web.lua diff --git a/chatgpt-web/luasrc/model/cbi/chatgpt.lua b/luci-app-chatgpt-web/luasrc/model/cbi/chatgpt.lua similarity index 100% rename from chatgpt-web/luasrc/model/cbi/chatgpt.lua rename to luci-app-chatgpt-web/luasrc/model/cbi/chatgpt.lua diff --git a/chatgpt-web/luasrc/view/chatgpt-web.htm b/luci-app-chatgpt-web/luasrc/view/chatgpt-web.htm similarity index 100% rename from chatgpt-web/luasrc/view/chatgpt-web.htm rename to luci-app-chatgpt-web/luasrc/view/chatgpt-web.htm diff --git a/chatgpt-web/po/zh-cn/chatgpt-web.po b/luci-app-chatgpt-web/po/zh-cn/chatgpt-web.po similarity index 100% rename from chatgpt-web/po/zh-cn/chatgpt-web.po rename to luci-app-chatgpt-web/po/zh-cn/chatgpt-web.po diff --git a/chatgpt-web/po/zh_Hans/chatgpt-web.po b/luci-app-chatgpt-web/po/zh_Hans/chatgpt-web.po similarity index 100% rename from chatgpt-web/po/zh_Hans/chatgpt-web.po rename to luci-app-chatgpt-web/po/zh_Hans/chatgpt-web.po diff --git a/chatgpt-web/root/etc/config/chatgpt-web b/luci-app-chatgpt-web/root/etc/config/chatgpt-web similarity index 100% rename from chatgpt-web/root/etc/config/chatgpt-web rename to luci-app-chatgpt-web/root/etc/config/chatgpt-web diff --git a/chatgpt-web/root/usr/share/rpcd/acl.d/luci-app-chatgpt-web.json b/luci-app-chatgpt-web/root/usr/share/rpcd/acl.d/luci-app-chatgpt-web.json similarity index 100% rename from chatgpt-web/root/usr/share/rpcd/acl.d/luci-app-chatgpt-web.json rename to luci-app-chatgpt-web/root/usr/share/rpcd/acl.d/luci-app-chatgpt-web.json diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/chatgpt-web.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/chatgpt-web.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/chatgpt-web.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/chatgpt-web.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/github-markdown-light.min.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/github-markdown-light.min.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/github-markdown-light.min.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/github-markdown-light.min.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/github.min.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/github.min.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/github.min.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/github.min.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/highlight.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/highlight.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/highlight.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/highlight.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/katex.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it-link-attributes.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it-link-attributes.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it-link-attributes.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it-link-attributes.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/markdown-it.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0default.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0default.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0default.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/mobile-drag-drop3.0.0-rc.0default.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/notyf.min.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh-TW.json b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh-TW.json similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh-TW.json rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh-TW.json diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh.json b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh.json similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh.json rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/prompts-zh.json diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/texmath.css b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/texmath.css similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/texmath.css rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/texmath.css diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/texmath.js b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/texmath.js similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/texmath.js rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/texmath.js diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/boy.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/boy.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/boy2.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy2.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/boy2.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy2.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/boy3.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy3.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/boy3.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/boy3.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoon.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoon.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoon.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoon.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy2.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy2.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy2.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoonboy2.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl2.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl2.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl2.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl2.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl3.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl3.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl3.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/cartoongirl3.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/dog.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/dog.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/dog.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/dog.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/girl.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/girl.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/girl2.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl2.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/girl2.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl2.jpg diff --git a/chatgpt-web/root/www/luci-static/chatgpt-web/user/girl3.jpg b/luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl3.jpg similarity index 100% rename from chatgpt-web/root/www/luci-static/chatgpt-web/user/girl3.jpg rename to luci-app-chatgpt-web/root/www/luci-static/chatgpt-web/user/girl3.jpg diff --git a/luci-app-eqosplus/Makefile b/luci-app-eqosplus/Makefile new file mode 100644 index 000000000..01a62966f --- /dev/null +++ b/luci-app-eqosplus/Makefile @@ -0,0 +1,26 @@ +# +# Copyright (C) 2006-2017 OpenWrt.org +# Copyright (C) 2022-2023 sirpdboy +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for eqosplus. +LUCI_DESCRIPTION:=LuCI support for Easy eqosplus(Support speed limit based on IP address). +LUCI_DEPENDS:=+bash +tc +kmod-sched-core +kmod-ifb +kmod-sched +iptables-mod-filter +iptables-mod-nat-extra +LUCI_PKGARCH:=all + +PKG_VERSION:=1.2.2 +PKG_RELEASE:=20230719 +PKG_MAINTAINER:=sirpdboy + +define Package/$(PKG_NAME)/conffiles +/etc/config/eqosplus +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature + diff --git a/luci-app-eqosplus/README.md b/luci-app-eqosplus/README.md new file mode 100644 index 000000000..f31c4bb59 --- /dev/null +++ b/luci-app-eqosplus/README.md @@ -0,0 +1,107 @@ +## 访问数:![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg)[![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) + +[eqosplus 定时限速插件](https://github.com/sirpdboy/luci-app-eqosplus) + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg) + +请 **认真阅读完毕** 本页面,本页面包含注意事项和如何使用。 + +## 功能说明: + +### 定时限速1.2.2版 +#### 2023.7.19 定时限速1.2.2:增加更多日期:工作日和休息日,自定义日期1,2,3中间用逗号分隔;加入MAC地址限速,从此不用担心IPV6和IPV4的限速问题。 + +### 定时限速1.0版 +#### 2022.12.24 定时限速在eqos的加强版,加入定时限制等功能。 + +## 编译使用方法 [![](https://img.shields.io/badge/-编译使用方法-F5F5F5.svg)](#编译使用方法-) + +将luci-app-eqosplus添加至 LEDE/OpenWRT 源码的方法。 + +### 下载源码方法一: +编辑源码文件夹根目录feeds.conf.default并加入如下内容: + +```Brach + # feeds获取源码: + src-git eqosplus https://github.com/sirpdboy/luci-app-eqosplus + ``` + ```Brach + # 更新feeds,并安装主题: + scripts/feeds update eqosplus + scripts/feeds install luci-app-eqosplus + ``` + +### 下载源码方法: + ```Brach + # 下载源码 + git clone https://github.com/sirpdboy/luci-app-eqosplus package/luci-app-eqosplus + make menuconfig + ``` +### 配置菜单 + ```Brach + make menuconfig + # 找到 LuCI -> Applications, 选择 luci-app-eqosplus, 保存后退出。 + ``` +### 编译 + ```Brach + # 编译固件 + make package/luci-app-eqosplus/compile V=s + ``` + +## 说明 [![](https://img.shields.io/badge/-说明-F5F5F5.svg)](#说明-) + +源码来源:https://github.com/sirpdboy/luci-app-eqosplus + + +- 你可以随意使用其中的源码,但请注明出处。 + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明2.jpg) + +## 界面 + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus.png) + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus2.png) + +## 使用与授权相关说明 + +- 本人开源的所有源码,任何引用需注明本处出处,如需修改二次发布必告之本人,未经许可不得做于任何商用用途。 + + +# My other project + +- 网络速度测试 :https://github.com/sirpdboy/NetSpeedTest + +- 任务设置(定时和开机二合一)插件 : https://github.com/sirpdboy/luci-app-autotimeset + +- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice + +- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd + +- kucat 主题: https://github.com/sirpdboy/luci-theme-kucat + +- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol + +- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced + +- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go + +- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd): https://github.com/sirpdboy/luci-app-advancedplus + +- 设置向导: https://github.com/sirpdboy/luci-app-wizard + +- 分区扩容: https://github.com/sirpdboy/luci-app-partexp + +- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy + +## 捐助 + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明3.jpg) + +| 图飞了😂 | 图飞了😂 | +| :-----------------: | :-------------: | +|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) | + + + 图飞了😂 + diff --git a/luci-app-eqosplus/luasrc/controller/eqosplus.lua b/luci-app-eqosplus/luasrc/controller/eqosplus.lua new file mode 100644 index 000000000..3a5b418dd --- /dev/null +++ b/luci-app-eqosplus/luasrc/controller/eqosplus.lua @@ -0,0 +1,16 @@ +module("luci.controller.eqosplus", package.seeall) +-- Copyright 2022-2023 sirpdboy +function index() + if not nixio.fs.access("/etc/config/eqosplus") then return end + entry({"admin", "control"}, firstchild(), "Control", 44).dependent = false + entry({"admin", "control", "eqosplus"}, cbi("eqosplus"), _("Eqosplus"), 10).dependent =true + entry({"admin", "control", "eqosplus", "status"}, call("act_status")).leaf = true +end + +function act_status() + local sys = require "luci.sys" + local e = {} + e.status = sys.call(" tc qdisc show | grep 'default' >/dev/null ") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) +end diff --git a/luci-app-eqosplus/luasrc/model/cbi/eqosplus.lua b/luci-app-eqosplus/luasrc/model/cbi/eqosplus.lua new file mode 100644 index 000000000..4eb89e62b --- /dev/null +++ b/luci-app-eqosplus/luasrc/model/cbi/eqosplus.lua @@ -0,0 +1,105 @@ +-- Copyright 2022-2023 sirpdboy +-- Licensed to the public under the Apache License 2.0. +local sys = require "luci.sys" +local ifaces = sys.net:devices() +local WADM = require "luci.tools.webadmin" +local ipc = require "luci.ip" +local a, t, e + +a = Map("eqosplus", translate("Network speed limit")) +a.description = translate("Users can limit the network speed for uploading/downloading through MAC, IP.The speed unit is MB/second.")..translate("Suggested feedback:")..translate("GitHub @sirpdboy/luci-app-eqosplus ") +a.template = "eqosplus/index" + +t = a:section(TypedSection, "eqosplus") +t.anonymous = true + +e = t:option(DummyValue, "eqosplus_status", translate("Status")) +e.template = "eqosplus/eqosplus" +e.value = translate("Collecting data...") + + +ipi = t:option(ListValue, "ifname", translate("Interface"), translate("Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode")) +ipi.default = "1" +ipi:value(1,translate("Automatic settings")) +ipi.rmempty = false +for _, v in pairs(ifaces) do + net = WADM.iface_get_network(v) + if net and net ~= "loopback" then + ipi:value(v) + end +end + +t = a:section(TypedSection, "device") +t.template = "cbi/tblsection" +t.anonymous = true +t.addremove = true + +e = t:option(Flag, "enable", translate("Enabled")) +e.rmempty = false +e.size = 4 + +ip = t:option(Value, "mac", translate("IP/MAC")) + +ipc.neighbors({family = 4, dev = "br-lan"}, function(n) + if n.mac and n.dest then + ip:value(n.dest:string(), "%s (%s)" %{ n.dest:string(), n.mac }) + end +end) +ipc.neighbors({family = 4, dev = "br-lan"}, function(n) + if n.mac and n.dest then + ip:value(n.mac, "%s (%s)" %{n.mac, n.dest:string() }) + end +end) + +e.size = 8 +dl = t:option(Value, "download", translate("Downloads")) +dl.default = '0.1' +dl.size = 4 + +ul = t:option(Value, "upload", translate("Uploads")) +ul.default = '0.1' +ul.size = 4 +function validate_time(self, value, section) + local hh, mm, ss + hh, mm, ss = string.match (value, "^(%d?%d):(%d%d)$") + hh = tonumber (hh) + mm = tonumber (mm) + if hh and mm and hh <= 23 and mm <= 59 then + return value + else + return nil, "Time HH:MM or space" + end +end + +e = t:option(Value, "timestart", translate("Start control time")) +e.placeholder = '00:00' +e.default = '00:00' +e.validate = validate_time +e.rmempty = true +e.size = 4 + +e = t:option(Value, "timeend", translate("Stop control time")) +e.placeholder = '00:00' +e.default = '00:00' +e.validate = validate_time +e.rmempty = true +e.size = 4 + +week=t:option(Value,"week",translate("Week Day(1~7)")) +week.rmempty = true +week:value('0',translate("Everyday")) +week:value(1,translate("Monday")) +week:value(2,translate("Tuesday")) +week:value(3,translate("Wednesday")) +week:value(4,translate("Thursday")) +week:value(5,translate("Friday")) +week:value(6,translate("Saturday")) +week:value(7,translate("Sunday")) +week:value('1,2,3,4,5',translate("Workday")) +week:value('6,7',translate("Rest Day")) +week.default='0' +week.size = 6 +comment = t:option(Value, "comment", translate("Comment")) +comment.size = 8 + +return a diff --git a/luci-app-eqosplus/luasrc/view/eqosplus/eqosplus.htm b/luci-app-eqosplus/luasrc/view/eqosplus/eqosplus.htm new file mode 100644 index 000000000..4445143fc --- /dev/null +++ b/luci-app-eqosplus/luasrc/view/eqosplus/eqosplus.htm @@ -0,0 +1,3 @@ +<%+cbi/valueheader%> +<%=pcdata(self:cfgvalue(section) or self.default or "")%> +<%+cbi/valuefooter%> \ No newline at end of file diff --git a/luci-app-eqosplus/luasrc/view/eqosplus/index.htm b/luci-app-eqosplus/luasrc/view/eqosplus/index.htm new file mode 100644 index 000000000..c4b8a072c --- /dev/null +++ b/luci-app-eqosplus/luasrc/view/eqosplus/index.htm @@ -0,0 +1,12 @@ +<% include("cbi/map") %> + \ No newline at end of file diff --git a/luci-app-eqosplus/po/zh-cn/eqosplus.po b/luci-app-eqosplus/po/zh-cn/eqosplus.po new file mode 100644 index 000000000..0fa563938 --- /dev/null +++ b/luci-app-eqosplus/po/zh-cn/eqosplus.po @@ -0,0 +1,61 @@ + +msgid "Eqosplus" +msgstr "定时限速" + +msgid "Network speed limit" +msgstr "定时网速限制" + +msgid "Running state" +msgstr "运行状态" + +msgid "Not running" +msgstr "未运行" + +msgid "Running" +msgstr "已运行" + +msgid "Users can limit the network speed for uploading/downloading through MAC, IP.The speed unit is MB/second." +msgstr "可以通过MAC,IP限制用户上传/下载的网速。速度单位为 MB/秒 。" + +msgid "MAC/IP" +msgstr "MAC/IP" + +msgid "Downloads" +msgstr "下载速度" + +msgid "Uploads" +msgstr "上传速度" + +msgid "Comment" +msgstr "备注" + +msgid "Upload bandwidth(Mbit/s)" +msgstr "上传总带宽(Mbit/s)" + +msgid "Download bandwidth(Mbit/s)" +msgstr "下载总带宽(Mbit/s)" + +msgid "Start control time" +msgstr "起控时间" + +msgid "Stop control time" +msgstr "停控时间" + +msgid "Week Day(1~7)" +msgstr "星期(1~7)" + +msgid "Suggested feedback:" +msgstr "建议反馈:" + +msgid "Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode" +msgstr "设定用来限制网络速度的接口,拨号使用pppoe-wan,DHCP模式使用WAN口的硬件接口(如:eth1),通用使用br-lan接口" + +msgid "Rest Day" +msgstr "休息日" + +msgid "Workday" +msgstr "工作日" + +msgid "Automatic settings" +msgstr "自动设置" + diff --git a/luci-app-eqosplus/po/zh_Hans b/luci-app-eqosplus/po/zh_Hans new file mode 100644 index 000000000..41451e4a1 --- /dev/null +++ b/luci-app-eqosplus/po/zh_Hans @@ -0,0 +1 @@ +zh-cn \ No newline at end of file diff --git a/luci-app-eqosplus/root/etc/config/eqosplus b/luci-app-eqosplus/root/etc/config/eqosplus new file mode 100644 index 000000000..2fc743957 --- /dev/null +++ b/luci-app-eqosplus/root/etc/config/eqosplus @@ -0,0 +1,27 @@ + +config eqosplus + option enabled '0' + option download '1000' + option upload '30' + option ifname '1' + +config device + option timestart '00:00' + + option week '0' + option download '1' + option timeend '23:55' + option upload '1' + option mac '' + option enable '0' + +config device + option mac '192.168.10.10/24' + option timestart '00:00' + option timeend '00:00' + + option week '0' + option download '0.1' + option upload '0.1' + option enable '0' + diff --git a/luci-app-eqosplus/root/etc/init.d/eqosplus b/luci-app-eqosplus/root/etc/init.d/eqosplus new file mode 100644 index 000000000..4e8c6896b --- /dev/null +++ b/luci-app-eqosplus/root/etc/init.d/eqosplus @@ -0,0 +1,54 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2023 sirpdboy herboy2008@gmail.com +# + +START=99 +USE_PROCD=1 + +NAME=eqosplus +LOCK="/var/lock/$NAME.lock" +CR=/etc/crontabs/root + +start_instance() { + procd_open_instance + procd_set_param command /usr/sbin/eqosplusctrl + procd_set_param respawn + procd_set_param stderr 1 + procd_close_instance +} + +_eqosplus_start() { + if [ "$(grep -c 'option enable .1.' /etc/config/$NAME 2>/dev/null)" -gt "0" ]; then + if [ x$(uci -q get turboacc.config.sw_flow) = 'x1' ] || [ x$(uci -q get turboacc.config.sfe_flow) = 'x1' ] ;then + uci -q set turboacc.config.sw_flow='0' + uci -q set turboacc.config.sfe_flow='0' + uci -q set turboacc.config.hw_flow='0' + uci commit turboacc + /etc/init.d/turboacc restart + fi + touch $LOCK + eqosplus start + start_instance + (crontab -l ; echo "00 1 * * * /etc/init.d/eqosplus start") | sort - | uniq - | crontab - + fi +} + +start_service(){ + [ -f $LOCK ] && exit + stop_service + _eqosplus_start + rm -f $LOCK +} + +service_triggers() { + procd_add_reload_trigger 'eqosplus' +} + +stop_service(){ + kill -9 $(busybox ps -w | grep 'eqosplusctrl' | grep -v 'grep' | awk '{print $1}') >/dev/null 2>&1 + sed -i '/eqosplus/d' $CR >/dev/null 2>&1 + rm -f $LOCK 2>/dev/null + eqosplus stop +} + diff --git a/luci-app-eqosplus/root/etc/uci-defaults/luci-eqosplus b/luci-app-eqosplus/root/etc/uci-defaults/luci-eqosplus new file mode 100644 index 000000000..07ff40ea5 --- /dev/null +++ b/luci-app-eqosplus/root/etc/uci-defaults/luci-eqosplus @@ -0,0 +1,13 @@ +#!/bin/sh +chmod +x /etc/init.d/eqosplus /usr/bin/eqosplus +uci -q batch <<-EOF >/dev/null + delete ucitrack.@eqosplus[-1] + add ucitrack eqosplus + set ucitrack.@eqosplus[-1].init=eqosplus + commit ucitrack +EOF + +[ -s /etc/config/eqosplus ] || echo "config eqosplus" > /etc/config/eqosplus + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/luci-app-eqosplus/root/usr/bin/eqosplus b/luci-app-eqosplus/root/usr/bin/eqosplus new file mode 100644 index 000000000..f859e5933 --- /dev/null +++ b/luci-app-eqosplus/root/usr/bin/eqosplus @@ -0,0 +1,242 @@ +#!/bin/bash + +# Copyright (C) 2006 OpenWrt.org +# Copyright 2022-2023 sirpdboy + +crrun=$1 +crid=$2 +NAME=eqosplus +IDLIST="/var/$NAME.idlist" +LOCK="/var/lock/$NAME.lock" +TMPID="/var/$NAME.tmpid" +if [ x$(uci get $NAME.@$NAME[0].ifname) = 'x1' ] ;then + dev=`ifconfig | grep "Point-to-Point" | cut -d " " -f1` + # [ ! ${dev} ] && dev=` uci -q get network.wan.ifname ` || dev=` uci -q get network.wan.device ` + [ ! ${dev} ] && dev=br-lan +else + dev=`uci -q get $NAME.@$NAME[0].ifname ` +fi +# [ x$(uci get eqosplus.@eqosplus[0].forced) = 'x1' ] && forced="default $id" || forced='default 999' +bin_iptables=$(which iptables) +bin_ip6tables=$(which ip6tables) +bin_tc=$(which tc) +bin_ebtables=$(which ebtables) +bin_ip=$(which ip) +# Uncomment this to debug commands +DEBUG=1 +## End +# Debug functions - echo + run +dbg_iptables() { + [ "${DEBUG:-0}" -eq 0 ] || echo "D: iptables $@" + $bin_iptables "$@" +} +dbg_ip6tables() { + [ "${DEBUG:-0}" -eq 0 ] || echo "D: ip6tables $@" + $bin_ip6tables "$@" +} + +dbg_tc() { + [ "${DEBUG:-0}" -eq 0 ] || echo "D: tc $@" + $bin_tc "$@" +} +dbg_ebtables() { + [ "${DEBUG:-0}" -eq 0 ] || echo "D: ebtables $@" + $bin_ebtables "$@" +} +dbg_ip() { + [ "${DEBUG:-0}" -eq 0 ] || echo "D: ip $@" + $bin_ip "$@" +} +is_macaddr() { + ret=1 + if echo "$1" | grep -qE '^([0-9A-Fa-f]{2}[-:]){5}[0-9A-Fa-f]{2}$'; then + ret=0 + fi + return $ret +} + +# Default commands +iptables="dbg_iptables" +ip6tables="dbg_ip6tables" +tc="dbg_tc" +ip="dbg_ip" +ebt="dbg_ebtables" +ipt=$iptables +ipt6=$ip6tables +ipt(){ + $iptables $@ + $ip6tables $@ +} + +iptm(){ + $iptables "-t mangle $@" + $ip6tables "-t mangle $@" +} + +stop_qos() { + + + for face in $( tc qdisc show | grep htb | awk '{print $5}');do + $tc qdisc del dev $face root + done + + $tc qdisc del dev ${dev} root 2>/dev/null + $tc qdisc del dev ${dev}_ifb root 2>/dev/null + $tc qdisc del dev ${dev} ingress 2>/dev/null + $ip link del dev ${dev}_ifb 2>/dev/null + +} + +init_qosplus() { + + $ip link add dev ${dev}_ifb name ${dev}_ifb type ifb + $ip link set dev ${dev}_ifb up + $tc qdisc add dev ${dev} root handle 1:0 htb default 1 + $tc class add dev ${dev} parent 1:0 classid 1:1 htb rate 80gbit prio 0 quantum 1500 + + $tc qdisc add dev ${dev}_ifb root handle 1:0 htb default 1 + $tc class add dev ${dev}_ifb parent 1:0 classid 1:1 htb rate 80gbit prio 0 quantum 1500 + + lanipaddr=$(uci -q get network.lan.ipaddr 2>/dev/null | awk -F '.' '{print $1"."$2"."$3".0/24"}') + $tc filter add dev $dev parent 1:0 protocol ipv4 prio 1 u32 match ip src "$lanipaddr" match ip dst "$lanipaddr" flowid 1:1 + $tc filter add dev ${dev}_ifb parent 1:0 protocol ipv4 prio 1 u32 match ip src "$lanipaddr" match ip dst "$lanipaddr" flowid 1:1 + + $tc qdisc add dev ${dev} ingress + $tc filter add dev ${dev} parent ffff: protocol all prio 2 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ${dev}_ifb +} +del_id() { + id=`expr $1 + 11 ` + [ "${DEBUG:-0}" -eq 0 ] || echo "D: del_id $@" "--$id --$mac" + $tc qd del dev ${dev} parent 1:$id + $tc qd del dev ${dev}_ifb parent 1:$id + + $tc class del dev ${dev} parent 1:1 classid 1:$id + $tc class del dev ${dev}_ifb parent 1:1 classid 1:$id + + $tc filter del dev ${dev}_ifb pref $id + $tc filter del dev ${dev} pref $id + + $tc filter del dev ${dev}_ifb pref 6 + $tc filter del dev ${dev} pref 6 + $tc filter del dev ${dev}_ifb pref 5 + $tc filter del dev ${dev} pref 5 + +} +add_mac() { + id=`expr $1 + 11 ` + A=`echo $mac | awk -F '[-:]' '{print $1$2}'` + B=`echo $mac | awk -F '[-:]' '{print $3$4$5$6}'` + C=`echo $mac | awk -F '[-:]' '{print $1$2$3$4}'` + D=`echo $mac | awk -F '[-:]' '{print $5$6}'` + [ "${DEBUG:-0}" -eq 0 ] || echo "D: add_mac $@ --id:$id --mac:$mac ABCD--$A-$B-$C-$D" + if [ "$UL" -gt 0 ]; then + $tc class add dev ${dev}_ifb parent 1:1 classid 1:$id htb rate "$UL"kbit ceil "$UL"kbit prio $id quantum 1500 + $tc qdisc add dev ${dev}_ifb parent 1:"$id" handle "$id": sfq perturb 1 + # $tc filter add dev ${dev}_ifb parent 1:0 protocol ip prio $id u32 match u16 0x0800 0xffff at -2 match u16 0x"$D" 0xffff at -4 match u32 0x"$C" 0xffffffff at -8 flowid 1:$id + # $tc filter add dev ${dev}_ifb parent 1: protocol ip prio $id u32 match u16 0x0800 0xFFFF at -2 match u32 0x"$C" 0xFFFFFFFF at -12 flowid 1:$id + $tc filter add dev ${dev}_ifb parent 1: protocol ip prio 1 u32 match u16 0xAAAA at 6 match u32 0x"$C" 0x"$B" police rate "$UL"kbit burst 10k drop flowid 1:$id + elif [ "$UL" == 0 ]; then + $tc filter add dev ${dev}_ifb parent 1:0 protocol ip prio 5 u32 match u16 0x0800 0xffff at -2 match u16 0x"$D" 0xffff at -4 match u32 0x"$C" 0xffffffff at -8 flowid 1:1 + fi + if [ "$DL" -gt 0 ]; then + $tc class add dev ${dev} parent 1:1 classid 1:$id htb rate "$DL"kbit ceil "$DL"kbit prio $id quantum 1500 + $tc qdisc add dev ${dev} parent 1:"$id" handle "$id": sfq perturb 1 + #$tc filter add dev ${dev} parent 1:0 protocol ip prio $id u32 match u16 0x0800 0xffff at -2 match u32 0x"$B" 0xffffffff at -12 match u16 0x"$A" 0xffff at -14 flowid 1:$id + # $tc filter add dev ${dev} parent 1: protocol ip prio $id u32 match u16 0x0800 0xFFFF at -2 match u32 0x"$C" 0xFFFFFFFF at -12 flowid 1:$id + $tc filter add dev ${dev} parent 1: protocol ip prio 1 u32 match u16 0xAAAA at 6 match u32 0x"$C" 0x"$B" police rate "$DL"kbit burst 10k drop flowid 1:$id + elif [ "$DL" == 0 ]; then + $tc filter add dev ${dev} parent 1:0 protocol ip prio 5 u32 match u16 0x0800 0xffff at -2 match u32 0x"$B" 0xffffffff at -12 match u16 0x"$A" 0xffff at -14 flowid 1:1 + fi +} + +add_ip() { + id=`expr $1 + 11 ` + # id=printf "%x\n" "$1" + [ "${DEBUG:-0}" -eq 0 ] || echo "D: add_ip $@ --$id --$mac" + Z=`echo $mac |awk -F '[/]' '{print $2}' ` + [ -n "$Z" ] && mac=`echo $mac |awk -F '[/]' '{print $1}' `|| Z=32 + if [ "$UL" -gt 0 ]; then + $tc class add dev ${dev}_ifb parent 1:1 classid 1:$id htb rate "$UL"kbit ceil "$UL"kbit prio $id quantum 1500 + $tc qdisc add dev ${dev}_ifb parent 1:"$id" handle "$id": sfq perturb 1 + $tc filter add dev ${dev}_ifb parent 1:0 prio $id protocol ip u32 match ip src "$mac"/"$Z" classid 1:$id + elif [ "$UL" == 0 ]; then + $tc filter add dev ${dev}_ifb parent 1:0 prio 6 protocol ip u32 match ip src "$mac"/"$Z" classid 1:1 + fi + if [ "$DL" -gt 0 ]; then + $tc class add dev ${dev} parent 1:1 classid 1:$id htb rate "$DL"kbit ceil "$DL"kbit prio $id quantum 1500 + $tc qdisc add dev ${dev} parent 1:"$id" handle "$id": sfq perturb 1 + $tc filter add dev ${dev} parent 1:0 prio $id protocol ip u32 match ip dst "$mac"/"$Z" classid 1:$id + elif [ "$DL" == 0 ]; then + $tc filter add dev ${dev} parent 1:0 prio 6 protocol ip u32 match ip dst "$mac"/"$Z" classid 1:1 + fi + + +} + +check_list() { + i=$1 + checki='0' + start_time=$(uci -q get $NAME.@device[$i].timestart 2>/dev/null) + end_time=$(uci -q get $NAME.@device[$i].timeend 2>/dev/null) + wweek=`uci -q get $NAME.@device[$i].week ` + current_time=$(date +%H:%M) + current_weekday=$(date +%u) + [ "$start_time" = "$end_time" ] || { + [[ "$start_time" < "$end_time" ]] && { [[ "$current_time" > "$start_time" ]] && [[ "$current_time" < "$end_time" ]] || return ; } + [[ "$start_time" > "$end_time" ]] && { [[ "$current_time" < "$start_time" ]] && [[ "$current_time" > "$end_time" ]] || return ; } + } + for ww in `echo $wweek | sed 's/,/ /g' `; do + if [ $current_weekday = $ww ] || [ "x0" = "x$ww" ] ; then + checki='1' + fi + done + return +} + +case "$crrun" in + "stop") + stop_qos + echo '' >$IDLIST + ;; + "start") + idlist=`uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sed -e 's/^/!/g' -e 's/$/!/g' > $IDLIST ;cat $IDLIST | sed -e 's/!//g' ` + # [ $idlist ] || /etc/init.d/eqosplus stop + cat $IDLIST + init_qosplus + checki='0' + for list in `echo $idlist | sed -e 's/!//g' ` ;do + check_list $list + if [ $checki == '1' ] ; then + mac=$(uci -q get $NAME.@device[$list].mac ) + DL=$(uci -q get $NAME.@device[$list].download 2>/dev/null | awk '{print $1*10^3/2}') + UL=$(uci -q get $NAME.@device[$list].upload 2>/dev/null | awk '{print $1*10^3/2}') + if is_macaddr $mac; then + add_mac $list + else + add_ip $list + fi + else + [ `cat $IDLIST 2>/dev/null | grep "!${list}!" | wc -l ` -gt 0 ] && { + del_id $list + sed -i "/!$list!/d" $IDLIST >/dev/null 2>&1 + } + fi + done + ;; + "add") + for list in `echo $crid | sed -e 's/!//g' | sed 's/,/ /g' ` ;do + mac=$(uci -q get $NAME.@device[$list].mac ) + DL=$(uci -q get $NAME.@device[$list].download 2>/dev/null | awk '{print $1*10^3/2}') + UL=$(uci -q get $NAME.@device[$list].upload 2>/dev/null | awk '{print $1*10^3/2}') + if is_macaddr $mac; then + add_mac $list + else + add_ip $list + fi + done + ;; + "del") + for list in `echo $crid | sed -e 's/!//g' | sed 's/,/ /g' ` ;do del_id $list; sed -i "/!$list!/d" $IDLIST >/dev/null 2>&1; done + ;; + +esac diff --git a/luci-app-eqosplus/root/usr/sbin/eqosplusctrl b/luci-app-eqosplus/root/usr/sbin/eqosplusctrl new file mode 100644 index 000000000..931fbf0fe --- /dev/null +++ b/luci-app-eqosplus/root/usr/sbin/eqosplusctrl @@ -0,0 +1,56 @@ +#!/bin/sh + +# Copyright (C) 2006 OpenWrt.org +# Copyright 2022-2023 sirpdboy +NAME=eqosplus +IDLIST="/var/$NAME.idlist" +TMPID="/var/$NAME.tmpid" + +idlist=`uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' ` + +check_list() { + i=$1 + checki='0' + start_time=$(uci -q get $NAME.@device[$i].timestart 2>/dev/null) + end_time=$(uci -q get $NAME.@device[$i].timeend 2>/dev/null) + wweek=`uci -q get $NAME.@device[$i].week ` + current_time=$(date +%H:%M) + current_weekday=$(date +%u) + [ "$start_time" = "$end_time" ] || { + [[ "$start_time" < "$end_time" ]] && { [[ "$current_time" > "$start_time" ]] && [[ "$current_time" < "$end_time" ]] || return ; } + [[ "$start_time" > "$end_time" ]] && { [[ "$current_time" < "$start_time" ]] && [[ "$current_time" > "$end_time" ]] || return ; } + } + for ww in `echo $wweek | sed 's/,/ /g' `; do + if [ $current_weekday = $ww ] || [ 'x0' = x$ww ] ;then + checki='1' + fi + done + return $re +} + +idlistusr(){ + + checki='0' + [ -s $IDLIST ] || return + for list in $idlist ;do + check_list $list + if [ $checki == '1' ] ; then + [ `cat $IDLIST 2>/dev/null | grep "!${list}!" | wc -l ` -gt 0 ] || { + eqosplus add $list + echo "!${list}!" >> $IDLIST ; cat $IDLIST | sort | uniq > $TMPID ;cat $TMPID >$IDLIST ;rm -rf $TMPID + } + else + [ `cat $IDLIST 2>/dev/null | grep "!${list}!" | wc -l ` -gt 0 ] && { + eqosplus del $list + sed -i "/!$list!/d" $IDLIST >/dev/null 2>&1 + } + fi + done +} + + +while :;do + sleep 30 + idlistusr + sleep 30 +done \ No newline at end of file diff --git a/luci-app-eqosplus/root/usr/share/rpcd/acl.d/luci-app-eqosplus.json b/luci-app-eqosplus/root/usr/share/rpcd/acl.d/luci-app-eqosplus.json new file mode 100644 index 000000000..b879f4de9 --- /dev/null +++ b/luci-app-eqosplus/root/usr/share/rpcd/acl.d/luci-app-eqosplus.json @@ -0,0 +1,11 @@ +{ + "luci-app-eqosplus": { + "description": "Grant UCI access for luci-app-eqosplus", + "read": { + "uci": [ "eqosplus" ] + }, + "write": { + "uci": [ "eqosplus" ] + } + } +} diff --git a/luci-app-eqosplus/界面.png b/luci-app-eqosplus/界面.png new file mode 100644 index 000000000..d346c698b Binary files /dev/null and b/luci-app-eqosplus/界面.png differ diff --git a/luci-app-netdata/Makefile b/luci-app-netdata/Makefile index de3b86671..b82a794bb 100644 --- a/luci-app-netdata/Makefile +++ b/luci-app-netdata/Makefile @@ -1,5 +1,6 @@ # Copyright (C) 2016 Openwrt.org -# +# Copyright (C) 2020-2021 sirpdboy +# https://github.com/sirpdboy/luci-app-netdata for v 1.33.1 cn # This is free software, licensed under the Apache License, Version 2.0 . # @@ -10,9 +11,14 @@ LUCI_DEPENDS:=+netdata LUCI_PKGARCH:=all PKG_NAME:=luci-app-netdata -PKG_VERSION:=1.0 -PKG_RELEASE:=3 +PKG_VERSION:=1.2 +PKG_RELEASE:=20240227 + +define Build/Compile +endef + include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature + diff --git a/luci-app-netdata/README.md b/luci-app-netdata/README.md index e76afbe9a..550bb08ed 100644 --- a/luci-app-netdata/README.md +++ b/luci-app-netdata/README.md @@ -1,31 +1,83 @@ -# luci-app-netdata -1.33.1 汉化(部分) -## 食用方法 -``` -rm -rf ./feeds/luci/applications/luci-app-netdata/ -git clone https://github.com/Jason6111/luci-app-netdata ./feeds/luci/applications/luci-app-netdata/ -``` -也可以用web里文件,覆盖至路径 `/usr/share/netdata/web/` ,请先备份 +### 访问数:[![](https://visitor-badge.glitch.me/badge?page_id=sirpdboy-visitor-badge)] [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ) - - dashboard_info.js - - dashboard.js - - main.js - - index.html - -已经开启了远程查看权,不需要的手动关闭 -`/root/etc/netdata/netdata.conf` -allow connections from = * -allow dashboard from = * -把这两行第一个*删除即可 -# 如发现luci进不去netdata请尝试 连接ssh进入openwrt输入以下指令 -``` -mkdir /usr/lib/lua/luci/view/netdata -mv -f /usr/lib/lua/luci/view/netdata.htm /usr/lib/lua/luci/view/netdata/netdata.htm +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg) -``` +[luci-app-netdata可控制的实时监控 ](https://github.com/sirpdboy/luci-app-netdata) +====================== -# 还原1.33.1(留作以后用) -``` -rm -rf ./feeds/packages/admin/netdata -git clone -b 1.33.1 https://github.com/Jason6111/luci-app-netdata ./feeds/packages/admin/netdata -``` +### 下载源码方法: + + ```Brach + + # 下载源码 + + git clone https://github.com/sirpdboy/luci-app-netdata package/luci-app-netdata + make menuconfig + + ``` +### 配置菜单 + + ```Brach + make menuconfig + # 找到 LuCI -> Applications, 选择 luci-app-netdata, 保存后退出。 + ``` + +### 编译 + + ```Brach + # 编译固件 + make package/luci-app-netdata/compile V=s + ``` + + +## 界面 +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/netdata1.jpg) + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/netdata2.jpg) + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明2.jpg) + + + +## 使用与授权相关说明 + +- 本人开源的所有源码,任何引用需注明本处出处,如需修改二次发布必告之本人,未经许可不得做于任何商用用途。 + + +# My other project + +- 网络速度测试 :https://github.com/sirpdboy/NetSpeedTest + +- 定时设置插件 : https://github.com/sirpdboy/luci-app-autotimeset + +- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice + +- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd + +- kucat 主题: https://github.com/sirpdboy/luci-theme-kucat + +- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol + +- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced + +- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go + +- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd): https://github.com/sirpdboy/luci-app-advancedplus + +- 设置向导: https://github.com/sirpdboy/luci-app-wizard + +- 分区扩容: https://github.com/sirpdboy/luci-app-partexp + +- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy + +## 捐助 + +![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明3.jpg) + +| 图飞了😂 | 图飞了😂 | +| :-----------------: | :-------------: | +|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) | + + + 图飞了😂 + diff --git a/luci-app-netdata/luasrc/controller/netdata.lua b/luci-app-netdata/luasrc/controller/netdata.lua index 3661c9654..0d89cd333 100644 --- a/luci-app-netdata/luasrc/controller/netdata.lua +++ b/luci-app-netdata/luasrc/controller/netdata.lua @@ -1,10 +1,24 @@ +-- Copyright (C) 2018-2022 sirpdboy https://github.com/sirpdboy/luci-app-netdata +-- Licensed to the public under the Apache License 2.0. + module("luci.controller.netdata", package.seeall) function index() - if not (luci.sys.call("pidof netdata > /dev/null") == 0) then + if not nixio.fs.access("/etc/config/netdata") then return end - local fs = require "nixio.fs" - - entry({"admin", "status", "netdata"}, template("netdata/netdata"), _("NetData"), 10).leaf = true + local e = entry({"admin", "status", "netdata"}, alias("admin", "status", "netdata", "setting"),_("NetData"), 10) + e.dependent = false + e.acl_depends = { "luci-app-netdata" } + entry({"admin", "status", "netdata", "setting"}, cbi("netdata/netdata"), _("Base Setting"), 20).leaf=true + entry({"admin", "status", "netdata", "netdata"}, template("netdata"), _("NetData"), 30).leaf = true + entry({"admin", "status", "netdata_status"}, call("act_status")) +end + +function act_status() + local sys = require "luci.sys" + local e = { } + e.running = sys.call("pidof netdata >/dev/null") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) end diff --git a/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua b/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua new file mode 100644 index 000000000..12469e9ae --- /dev/null +++ b/luci-app-netdata/luasrc/model/cbi/netdata/netdata.lua @@ -0,0 +1,25 @@ +-- Copyright 2018-2022 sirpdboy (herboy2008@gmail.com) +-- https://github.com/sirpdboy/luci-app-netdata +require("luci.util") + +local m, s ,o + +m = Map("netdata", translate("NetData"), translate("Netdata is high-fidelity infrastructure monitoring and troubleshooting.Open-source, free, preconfigured, opinionated, and always real-time.")..translate("
For specific usage, see:")..translate("GitHub @sirpdboy/luci-app-netdata ") ) +m:section(SimpleSection).template = "netdata_status" +s = m:section(TypedSection, "netdata", translate("Global Settings")) +s.addremove=false +s.anonymous=true + +o=s:option(Flag,"enabled",translate("Enable")) +o.default=0 + +o=s:option(Value, "port",translate("Set the netdata access port")) +o.datatype="uinteger" +o.default=19999 + +m.apply_on_parse = true +m.on_after_apply = function(self,map) + luci.sys.exec("/etc/init.d/netdata start") +end + +return m diff --git a/luci-app-netdata/luasrc/model/cgi/netdate.lua b/luci-app-netdata/luasrc/model/cgi/netdate.lua deleted file mode 100644 index 7a7f47d65..000000000 --- a/luci-app-netdata/luasrc/model/cgi/netdate.lua +++ /dev/null @@ -1,25 +0,0 @@ --- Copyright 2022 Jason - -function index() - - -o = Map("netdate", "" .. translate("实时监控") .."", "" .. translate( "强大的实时监控数据,需要中文版请点击:【升级中文版】") .."") - -t = o:section(TypedSection, "netdate") -t.anonymous = true -t.description = translate(string.format("%s

", status)) - -t:tab("base",translate("Basic Settings")) - -e = t:taboption("base", Button, "restart", translate("手动更新")) -e.inputtitle = translate("升级中文版") -e.inputstyle = "reload" -e.write = function() - luci.sys.call("/usr/share/netdata/netdatacn 2>&1 >/dev/null") - luci.http.redirect(luci.dispatcher.build_url("admin","status","netdata")) -end - -t=o:section(TypedSection,"rss_rules",translate("技术支持")) -t.anonymous = true -t:append(Template("feedback")) -return o diff --git a/luci-app-netdata/luasrc/view/netdata.htm b/luci-app-netdata/luasrc/view/netdata.htm new file mode 100644 index 000000000..9fc5496eb --- /dev/null +++ b/luci-app-netdata/luasrc/view/netdata.htm @@ -0,0 +1,33 @@ +<%# + Copyright 2008-2024 by sirpdboy + https://github.com/sirpdboy/luci-app-netdata + Licensed to the public under the Apache License 2.0. +-%> +<% + local running = luci.sys.exec("pidof netdata | awk -F ' ' '{print $1}'") +%> +<%+header%> + +
+ +<% if tonumber(running) ~= nil then %> + + +
+ +<% else %> + +
+ +

<%:The Netdata service is not running.%>

+

<%:Please enable the NetData service%>

+
+<% end -%> + +<%+footer%> diff --git a/luci-app-netdata/luasrc/view/netdata/netdata.htm b/luci-app-netdata/luasrc/view/netdata/netdata.htm deleted file mode 100644 index 47bedac54..000000000 --- a/luci-app-netdata/luasrc/view/netdata/netdata.htm +++ /dev/null @@ -1,16 +0,0 @@ -<%+header%> -
-

<%=translate("NetData")%>

- - - -
- -<%+footer%> diff --git a/luci-app-netdata/luasrc/view/netdata_status.htm b/luci-app-netdata/luasrc/view/netdata_status.htm new file mode 100644 index 000000000..318a02820 --- /dev/null +++ b/luci-app-netdata/luasrc/view/netdata_status.htm @@ -0,0 +1,25 @@ + + +
+ <%:NetData Status%> +

+ <%:Collecting data...%> +

+
\ No newline at end of file diff --git a/luci-app-netdata/po/zh-cn/netdata.po b/luci-app-netdata/po/zh-cn/netdata.po index e8966b815..52e2cc894 100644 --- a/luci-app-netdata/po/zh-cn/netdata.po +++ b/luci-app-netdata/po/zh-cn/netdata.po @@ -1,5 +1,32 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" +msgid "Running state" +msgstr "运行状态" + +msgid "The Netdata service is running." +msgstr "NetData服务已启动" + +msgid "The Netdata service is not running." +msgstr "NetData服务未启动" + +msgid "NetData Status" +msgstr "NetData服务状态" + +msgid "Please enable the NetData service" +msgstr "请将NetData服务启用" + msgid "NetData" msgstr "实时监控" + +msgid "Set the netdata access port" +msgstr "设置访问端口" + +msgid "Base Setting" +msgstr "基本设置" + +msgid "Netdata is high-fidelity infrastructure monitoring and troubleshooting.Open-source, free, preconfigured, opinionated, and always real-time." +msgstr "Netdata是高保真的基础设施监控和故障排除。开源、免费、预配置、始终实时." + +msgid "
For specific usage, see:" +msgstr "
具体使用方法参见:" diff --git a/luci-app-netdata/po/zh_Hans b/luci-app-netdata/po/zh_Hans deleted file mode 120000 index 41451e4a1..000000000 --- a/luci-app-netdata/po/zh_Hans +++ /dev/null @@ -1 +0,0 @@ -zh-cn \ No newline at end of file diff --git a/luci-app-netdata/po/zh_Hans/netdata.po b/luci-app-netdata/po/zh_Hans/netdata.po new file mode 100644 index 000000000..52e2cc894 --- /dev/null +++ b/luci-app-netdata/po/zh_Hans/netdata.po @@ -0,0 +1,32 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Running state" +msgstr "运行状态" + +msgid "The Netdata service is running." +msgstr "NetData服务已启动" + +msgid "The Netdata service is not running." +msgstr "NetData服务未启动" + +msgid "NetData Status" +msgstr "NetData服务状态" + +msgid "Please enable the NetData service" +msgstr "请将NetData服务启用" + +msgid "NetData" +msgstr "实时监控" + +msgid "Set the netdata access port" +msgstr "设置访问端口" + +msgid "Base Setting" +msgstr "基本设置" + +msgid "Netdata is high-fidelity infrastructure monitoring and troubleshooting.Open-source, free, preconfigured, opinionated, and always real-time." +msgstr "Netdata是高保真的基础设施监控和故障排除。开源、免费、预配置、始终实时." + +msgid "
For specific usage, see:" +msgstr "
具体使用方法参见:" diff --git a/luci-app-netdata/root/etc/config/netdata b/luci-app-netdata/root/etc/config/netdata new file mode 100644 index 000000000..83f9eda99 --- /dev/null +++ b/luci-app-netdata/root/etc/config/netdata @@ -0,0 +1,6 @@ + +config netdata 'netdata' + option logger '1' + option enabled '0' + option port '19990' + diff --git a/luci-app-netdata/root/etc/netdata/apps_groups.conf b/luci-app-netdata/root/etc/netdata/apps_groups.conf deleted file mode 100644 index d326be78f..000000000 --- a/luci-app-netdata/root/etc/netdata/apps_groups.conf +++ /dev/null @@ -1,314 +0,0 @@ -# -# apps.plugin process grouping -# -# The apps.plugin displays charts with information about the processes running. -# This config allows grouping processes together, so that several processes -# will be reported as one. -# -# Only groups in this file are reported. All other processes will be reported -# as 'other'. -# -# For each process given, its whole process tree will be grouped, not just -# the process matched. The plugin will include both parents and childs. -# -# The format is: -# -# group: process1 process2 process3 ... -# -# 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". -# -# Wildcard support: -# 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 processes with single quotes, enclose them in double quotes -# example: "process with this ' single quote" -# -# To add processes with double quotes, enclose them in single quotes: -# example: 'process with this " double quote' -# -# If a group or process name starts with a -, the dimension will be hidden -# (cpu chart only). -# -# 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 -# affect the charts generated. However, producing charts with hundreds of -# dimensions may slow down your web browser. -# -# The order of the entries in this list is important: the first that matches -# a process is used, so put important ones at the top. Processes not matched -# by any row, will inherit it from their parents or children. -# -# The order also controls the order of the dimensions on the generated charts -# (although applications started after apps.plugin is started, will be appended -# to the existing list of dimensions the netdata daemon maintains). - -# ----------------------------------------------------------------------------- -# NETDATA processes accounting - -# netdata main process -netdata: netdata - -# netdata known plugins -# plugins not defined here will be accumulated in netdata, above -apps.plugin: apps.plugin -freeipmi.plugin: freeipmi.plugin -nfacct.plugin: nfacct.plugin -cups.plugin: cups.plugin -xenstat.plugin: xenstat.plugin -perf.plugin: perf.plugin -charts.d.plugin: *charts.d.plugin* -node.d.plugin: *node.d.plugin* -python.d.plugin: *python.d.plugin* -tc-qos-helper: *tc-qos-helper.sh* -fping: fping -ioping: ioping -go.d.plugin: *go.d.plugin* -slabinfo.plugin: slabinfo.plugin -ebpf.plugin: *ebpf.plugin* - -# agent-service-discovery -agent_sd: agent_sd - -# ----------------------------------------------------------------------------- -# authentication/authorization related servers - -auth: radius* openldap* ldap* slapd authelia -fail2ban: fail2ban* - -# ----------------------------------------------------------------------------- -# web/ftp servers - -httpd: apache* httpd nginx* lighttpd hiawatha -proxy: squid* c-icap squidGuard varnish* -php: php* lsphp* -ftpd: proftpd in.tftpd vsftpd -uwsgi: uwsgi -unicorn: *unicorn* -puma: *puma* - -# ----------------------------------------------------------------------------- -# database servers - -sql: mysqld* mariad* postgres* postmaster* oracle_* ora_* sqlservr -nosql: mongod redis* memcached *couchdb* -timedb: prometheus *carbon-cache.py* *carbon-aggregator.py* *graphite/manage.py* *net.opentsdb.tools.TSDMain* influxd* -columndb: clickhouse-server* - -# ----------------------------------------------------------------------------- -# email servers - -email: dovecot imapd pop3d amavis* master zmstat* zmmailboxdmgr qmgr oqmgr saslauthd opendkim clamd freshclam tlsmgr postfwd2 postscreen postfix smtp* lmtp* sendmail - -# ----------------------------------------------------------------------------- -# network, routing, VPN - -ppp: ppp* -vpn: openvpn pptp* cjdroute gvpe tincd -wifi: hostapd wpa_supplicant NetworkManager -routing: ospfd* ospf6d* bgpd bfdd fabricd isisd eigrpd sharpd staticd ripd ripngd pimd pbrd nhrpd ldpd zebra vrrpd vtysh bird* -modem: ModemManager -tor: tor - -# ----------------------------------------------------------------------------- -# high availability and balancers - -camo: *camo* -balancer: ipvs_* haproxy -ha: corosync hs_logd ha_logd stonithd pacemakerd lrmd crmd - -# ----------------------------------------------------------------------------- -# telephony - -pbx: asterisk safe_asterisk *vicidial* -sip: opensips* stund - -# ----------------------------------------------------------------------------- -# chat - -chat: irssi *vines* *prosody* murmurd - -# ----------------------------------------------------------------------------- -# monitoring - -logs: ulogd* syslog* rsyslog* logrotate systemd-journald rotatelogs -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 *auoms* *omsagent* - -# ----------------------------------------------------------------------------- -# storage, file systems and file servers - -ceph: ceph-* ceph_* radosgw* rbd-* cephfs-* osdmaptool crushtool -samba: smbd nmbd winbindd ctdbd ctdb-* ctdb_* -nfs: rpcbind rpc.* nfs* -zfs: spl_* z_* txg_* zil_* arc_* l2arc* -btrfs: btrfs* -iscsi: iscsid iscsi_eh - -# ----------------------------------------------------------------------------- -# kubernetes - -kubelet: kubelet -kube-dns: kube-dns -kube-proxy: kube-proxy -metrics-server: metrics-server -heapster: heapster - -# ----------------------------------------------------------------------------- -# containers & virtual machines - -containers: lxc* docker* balena* -VMs: vbox* VBox* qemu* - -# ----------------------------------------------------------------------------- -# ssh servers and clients - -ssh: ssh* scp dropbear - -# ----------------------------------------------------------------------------- -# print servers and clients - -print: cups* lpd lpq - -# ----------------------------------------------------------------------------- -# time servers and clients - -time: ntp* systemd-timesyn* chronyd - -# ----------------------------------------------------------------------------- -# dhcp servers and clients - -dhcp: *dhcp* - -# ----------------------------------------------------------------------------- -# name servers and clients - -dns: named unbound nsd pdns_server knotd gdnsd yadifad dnsmasq systemd-resolve* -dnsdist: dnsdist - -# ----------------------------------------------------------------------------- -# installation / compilation / debugging - -build: cc1 cc1plus as gcc* cppcheck ld make cmake automake autoconf autoreconf -build: git gdb valgrind* - -# ----------------------------------------------------------------------------- -# antivirus - -antivirus: clam* *clam imunify360* - -# ----------------------------------------------------------------------------- -# torrent clients - -torrents: *deluge* transmission* *SickBeard* *CouchPotato* *rtorrent* - -# ----------------------------------------------------------------------------- -# backup servers and clients - -backup: rsync lsyncd bacula* borg rclone - -# ----------------------------------------------------------------------------- -# cron - -cron: cron* atd anacron systemd-cron* - -# ----------------------------------------------------------------------------- -# UPS - -ups: upsmon upsd */nut/* - -# ----------------------------------------------------------------------------- -# media players, servers, clients - -media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd -media: mpd minidlnad mt-daapd avahi* Plex* jellyfin squeeze* jackett Ombi - -# ----------------------------------------------------------------------------- -# java applications - -hdfsdatanode: *org.apache.hadoop.hdfs.server.datanode.DataNode* -hdfsnamenode: *org.apache.hadoop.hdfs.server.namenode.NameNode* -hdfsjournalnode: *org.apache.hadoop.hdfs.qjournal.server.JournalNode* -hdfszkfc: *org.apache.hadoop.hdfs.tools.DFSZKFailoverController* - -yarnnode: *org.apache.hadoop.yarn.server.nodemanager.NodeManager* -yarnmgr: *org.apache.hadoop.yarn.server.resourcemanager.ResourceManager* -yarnproxy: *org.apache.hadoop.yarn.server.webproxy.WebAppProxyServer* - -sparkworker: *org.apache.spark.deploy.worker.Worker* -sparkmaster: *org.apache.spark.deploy.master.Master* - -hbaseregion: *org.apache.hadoop.hbase.regionserver.HRegionServer* -hbaserest: *org.apache.hadoop.hbase.rest.RESTServer* -hbasethrift: *org.apache.hadoop.hbase.thrift.ThriftServer* -hbasemaster: *org.apache.hadoop.hbase.master.HMaster* - -zookeeper: *org.apache.zookeeper.server.quorum.QuorumPeerMain* - -hive2: *org.apache.hive.service.server.HiveServer2* -hivemetastore: *org.apache.hadoop.hive.metastore.HiveMetaStore* - -solr: *solr.install.dir* - -airflow: *airflow* - -# ----------------------------------------------------------------------------- -# X - -X: X Xorg xinit lightdm xdm pulseaudio gkrellm xfwm4 xfdesktop xfce* Thunar -X: xfsettingsd xfconfd gnome-* gdm gconf* dconf* xfconf* *gvfs gvfs* slim -X: kdeinit* kdm plasmashell -X: evolution-* firefox chromium opera vivaldi-bin epiphany WebKit* -X: '*systemd --user*' chrome *chrome-sandbox* *google-chrome* *chromium* *firefox* - -# ----------------------------------------------------------------------------- -# 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 - -kernel: kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod -kernel: fsnotify_mark kthrotld deferwq scsi_* - -# ----------------------------------------------------------------------------- -# other application servers - -kafka: *kafka.Kafka* - -rabbitmq: *rabbitmq* - -sidekiq: *sidekiq* -java: java -ipfs: ipfs - -node: node* -factorio: factorio - -p4: p4* - -git-services: gitea gitlab-runner diff --git a/luci-app-netdata/root/etc/netdata/ebpf.conf b/luci-app-netdata/root/etc/netdata/ebpf.conf deleted file mode 100644 index d9b6b9393..000000000 --- a/luci-app-netdata/root/etc/netdata/ebpf.conf +++ /dev/null @@ -1,7 +0,0 @@ -[global] - ebpf load mode = entry - disable apps = no - -[ebpf programs] - process = yes - network viewer = yes diff --git a/luci-app-netdata/root/etc/netdata/exporting.conf b/luci-app-netdata/root/etc/netdata/exporting.conf deleted file mode 100644 index 9c1e7ffb7..000000000 --- a/luci-app-netdata/root/etc/netdata/exporting.conf +++ /dev/null @@ -1,88 +0,0 @@ -[exporting:global] - enabled = no - # send configured labels = yes - # send automatic labels = no - # update every = 10 - -[prometheus:exporter] - # send names instead of ids = yes - # send configured labels = yes - # send automatic labels = no - # send charts matching = * - # send hosts matching = localhost * - # prefix = netdata - -# An example configuration for graphite, json, opentsdb exporting connectors -# [graphite:my_graphite_instance] - # enabled = no - # destination = localhost - # data source = average - # prefix = netdata - # hostname = my_hostname - # update every = 10 - # buffer on failures = 10 - # timeout ms = 20000 - # send names instead of ids = yes - # send charts matching = * - # send hosts matching = localhost * - -# [prometheus_remote_write:my_prometheus_remote_write_instance] - # enabled = no - # destination = localhost - # remote write URL path = /receive - # data source = average - # prefix = netdata - # hostname = my_hostname - # update every = 10 - # buffer on failures = 10 - # timeout ms = 20000 - # send names instead of ids = yes - # send charts matching = * - # send hosts matching = localhost * - -# [kinesis:my_kinesis_instance] - # enabled = no - # destination = us-east-1 - # stream name = netdata - # aws_access_key_id = my_access_key_id - # aws_secret_access_key = my_aws_secret_access_key - # data source = average - # prefix = netdata - # hostname = my_hostname - # update every = 10 - # buffer on failures = 10 - # timeout ms = 20000 - # send names instead of ids = yes - # send charts matching = * - # send hosts matching = localhost * - -# [pubsub:my_pubsub_instance] - # enabled = no - # destination = pubsub.googleapis.com - # credentials file = /etc/netdata/pubsub_credentials.json - # project id = my_project - # topic id = my_topic - # data source = average - # prefix = netdata - # hostname = my_hostname - # update every = 10 - # buffer on failures = 10 - # timeout ms = 20000 - # send names instead of ids = yes - # send charts matching = * - # send hosts matching = localhost * - -# [mongodb:my_mongodb_instance] - # enabled = no - # destination = localhost - # database = my_database - # collection = my_collection - # data source = average - # prefix = netdata - # hostname = my_hostname - # update every = 10 - # buffer on failures = 10 - # timeout ms = 20000 - # send names instead of ids = yes - # send charts matching = * - # send hosts matching = localhost * diff --git a/luci-app-netdata/root/etc/netdata/netdata.conf b/luci-app-netdata/root/etc/netdata/netdata.conf deleted file mode 100644 index 30028497f..000000000 --- a/luci-app-netdata/root/etc/netdata/netdata.conf +++ /dev/null @@ -1,32 +0,0 @@ -# Full configuration can be retrieved from the running -# server at http://localhost:19999/netdata.conf -# -# Example: -# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf -# - -[global] - update every = 2 - memory deduplication (ksm) = no - debug log = syslog - error log = syslog - access log = none - run as user = root - -[web] - allow connections from = * localhost 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.* - allow dashboard from = * localhost 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.* - -[plugins] - cgroups = no - apps = no - charts.d = no - fping = no - node.d = no - python.d = no - -[health] - enabled = no - -[plugin:proc:ipc] - shared memory totals = no diff --git a/luci-app-netdata/root/etc/netdata/stream.conf b/luci-app-netdata/root/etc/netdata/stream.conf deleted file mode 100644 index b5142632d..000000000 --- a/luci-app-netdata/root/etc/netdata/stream.conf +++ /dev/null @@ -1,205 +0,0 @@ -# netdata configuration for aggregating data from remote hosts -# -# API keys authorize a pair of sending-receiving netdata servers. -# Once their communication is authorized, they can exchange metrics for any -# number of hosts. -# -# You can generate API keys, with the linux command: uuidgen - - -# ----------------------------------------------------------------------------- -# 1. ON CHILD NETDATA - THE ONE THAT WILL BE SENDING METRICS - -[stream] - # Enable this on child nodes, to have them send metrics. - enabled = no - - # Where is the receiving netdata? - # A space separated list of: - # - # [PROTOCOL:]HOST[%INTERFACE][:PORT][:SSL] - # - # If many are given, the first available will get the metrics. - # - # PROTOCOL = tcp, udp, or unix (only tcp and unix are supported by parent nodes) - # HOST = an IPv4, IPv6 IP, or a hostname, or a unix domain socket path. - # IPv6 IPs should be given with brackets [ip:address] - # INTERFACE = the network interface to use (only for IPv6) - # PORT = the port number or service name (/etc/services) - # SSL = when this word appear at the end of the destination string - # the Netdata will encrypt the connection with the parent. - # - # This communication is not HTTP (it cannot be proxied by web proxies). - destination = - - # Skip Certificate verification? - # - # The netdata child is configurated to avoid invalid SSL/TLS certificate, - # so certificates that are self-signed or expired will stop the streaming. - # Case the server certificate is not valid, you can enable the use of - # 'bad' certificates setting the next option as 'yes'. - # - #ssl skip certificate verification = yes - - # Certificate Authority Path - # - # OpenSSL has a default directory where the known certificates are stored, - # case it is necessary it is possible to change this rule using the variable - # "CApath" - # - #CApath = /etc/ssl/certs/ - - # Certificate Authority file - # - # When the Netdata parent has certificate, that is not recognized as valid, - # we can add this certificate in the list of known certificates in CApath - # and give for Netdata as argument. - # - #CAfile = /etc/ssl/certs/cert.pem - - # The API_KEY to use (as the sender) - api key = - - # The timeout to connect and send metrics - timeout seconds = 60 - - # If the destination line above does not specify a port, use this - default port = 19999 - - # filter the charts to be streamed - # netdata SIMPLE PATTERN: - # - space separated list of patterns (use \ to include spaces in patterns) - # - use * as wildcard, any number of times within each pattern - # - prefix a pattern with ! for a negative match (ie not stream the charts it matches) - # - the order of patterns is important (left to right) - # To send all except a few, use: !this !that * (ie append a wildcard pattern) - send charts matching = * - - # The buffer to use for sending metrics. - # 1MB is good for 10-20 seconds of data, so increase this if you expect latencies. - # The buffer is flushed on reconnects (this will not prevent gaps at the charts). - buffer size bytes = 1048576 - - # If the connection fails, or it disconnects, - # retry after that many seconds. - reconnect delay seconds = 5 - - # Sync the clock of the charts for that many iterations, when starting. - initial clock resync iterations = 60 - -# ----------------------------------------------------------------------------- -# 2. ON PARENT NETDATA - THE ONE THAT WILL BE RECEIVING METRICS - -# You can have one API key per child, -# or the same API key for all child nodes. -# -# netdata searches for options in this order: -# -# a) parent netdata settings (netdata.conf) -# b) [stream] section (above) -# c) [API_KEY] section (below, settings for the API key) -# d) [MACHINE_GUID] section (below, settings for each machine) -# -# You can combine the above (the more specific setting will be used). - -# API key authentication -# If the key is not listed here, it will not be able to push metrics. - -# [API_KEY] is [YOUR-API-KEY], i.e [11111111-2222-3333-4444-555555555555] -[API_KEY] - # Default settings for this API key - - # You can disable the API key, by setting this to: no - # The default (for unknown API keys) is: no - enabled = no - - # A list of simple patterns matching the IPs of the servers that - # will be pushing metrics using this API key. - # The metrics are received via the API port, so the same IPs - # should also be matched at netdata.conf [web].allow connections from - allow from = * - - # The default history in entries, for all hosts using this API key. - # You can also set it per host below. - # If you don't set it here, the history size of the central netdata - # will be used. - default history = 3600 - - # The default memory mode to be used for all hosts using this API key. - # You can also set it per host below. - # If you don't set it here, the memory mode of netdata.conf will be used. - # Valid modes: - # save save on exit, load on start - # map like swap (continuously syncing to disks - you need SSD) - # ram keep it in RAM, don't touch the disk - # none no database at all (use this on headless proxies) - # dbengine like a traditional database - default memory mode = ram - - # Shall we enable health monitoring for the hosts using this API key? - # 3 possible values: - # yes enable alarms - # no do not enable alarms - # auto enable alarms, only when the sending netdata is connected. For ephemeral child nodes or child system restarts, - # ensure that the netdata process on the child is gracefully stopped, to prevent invalid last_collected alarms - # You can also set it per host, below. - # The default is taken from [health].enabled of netdata.conf - health enabled by default = auto - - # postpone alarms for a short period after the sender is connected - default postpone alarms on connect seconds = 60 - - # need to route metrics differently? set these. - # the defaults are the ones at the [stream] section (above) - #default proxy enabled = yes | no - #default proxy destination = IP:PORT IP:PORT ... - #default proxy api key = API_KEY - #default proxy send charts matching = * - - -# ----------------------------------------------------------------------------- -# 3. PER SENDING HOST SETTINGS, ON PARENT NETDATA -# THIS IS OPTIONAL - YOU DON'T HAVE TO CONFIGURE IT - -# This section exists to give you finer control of the parent settings for each -# child host, when the same API key is used by many netdata child nodes / proxies. -# -# Each netdata has a unique GUID - generated the first time netdata starts. -# You can find it at /var/lib/netdata/registry/netdata.public.unique.id -# (at the child). -# -# The host sending data will have one. If the host is not ephemeral, -# you can give settings for each sending host here. - -[MACHINE_GUID] - # enable this host: yes | no - # When disabled, the parent will not receive metrics for this host. - # THIS IS NOT A SECURITY MECHANISM - AN ATTACKER CAN SET ANY OTHER GUID. - # Use only the API key for security. - enabled = no - - # A list of simple patterns matching the IPs of the servers that - # will be pushing metrics using this MACHINE GUID. - # The metrics are received via the API port, so the same IPs - # should also be matched at netdata.conf [web].allow connections from - # and at stream.conf [API_KEY].allow from - allow from = * - - # The number of entries in the database - history = 3600 - - # The memory mode of the database: save | map | ram | none | dbengine - memory mode = save - - # Health / alarms control: yes | no | auto - health enabled = yes - - # postpone alarms when the sender connects - postpone alarms on connect seconds = 60 - - # need to route metrics differently? - # the defaults are the ones at the [API KEY] section - #proxy enabled = yes | no - #proxy destination = IP:PORT IP:PORT ... - #proxy api key = API_KEY - #proxy send charts matching = * diff --git a/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata b/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata index 83bf1cd80..6c86c538a 100644 --- a/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata +++ b/luci-app-netdata/root/etc/uci-defaults/40_luci-app-netdata @@ -1,8 +1,11 @@ #!/bin/sh +[ -f /usr/share/netdata/webcn/netdata.conf ] && mv -f /usr/share/netdata/webcn/netdata.conf /etc/netdata/netdata.conf +[ -f /usr/share/netdata/webcn/netdata ] && mv -f /usr/share/netdata/webcn/netdata /etc/config/netdata [ -f /usr/share/netdata/webcn/dashboard.js ] && mv -f /usr/share/netdata/webcn/dashboard.js /usr/share/netdata/web/dashboard.js [ -f /usr/share/netdata/webcn/dashboard_info.js ] && mv -f /usr/share/netdata/webcn/dashboard_info.js /usr/share/netdata/web/dashboard_info.js [ -f /usr/share/netdata/webcn/main.js ] && mv -f /usr/share/netdata/webcn/main.js /usr/share/netdata/web/main.js [ -f /usr/share/netdata/webcn/index.html ] && mv -f /usr/share/netdata/webcn/index.html /usr/share/netdata/web/index.html - +[ -f /usr/share/netdata/webcn/netdata.init ] && mv -f /usr/share/netdata/webcn/netdata.init /etc/init.d/netdata rm -rf /tmp/luci-modulecache /tmp/luci-indexcache* +chmod +x /etc/init.d/netdata exit 0 diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js b/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js index 9642d3ac0..1065e771e 100644 --- a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js +++ b/luci-app-netdata/root/usr/share/netdata/webcn/dashboard.js @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -// 时间 By Jason + // DO NOT EDIT: This file is automatically generated from the source files in src/ // ---------------------------------------------------------------------------- @@ -142,18 +142,18 @@ NETDATA.zeropad = function (x) { NETDATA.seconds4human = function (seconds, options) { let defaultOptions = { - now: '现在', + now: 'now', space: ' ', - negative_suffix: '前', - day: '日', - days: '日', - hour: '小时', - hours: '小时', - minute: '分钟', - minutes: '分钟', - second: '秒', - seconds: '秒', - and: '及' + negative_suffix: 'ago', + day: 'day', + days: 'days', + hour: 'hour', + hours: 'hours', + minute: 'min', + minutes: 'mins', + second: 'sec', + seconds: 'secs', + and: 'and' }; if (typeof options !== 'object') { diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard_info.js b/luci-app-netdata/root/usr/share/netdata/webcn/dashboard_info.js index 08dcafbca..c4e55fa2d 100644 --- a/luci-app-netdata/root/usr/share/netdata/webcn/dashboard_info.js +++ b/luci-app-netdata/root/usr/share/netdata/webcn/dashboard_info.js @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -// 翻译 By Jason + // Codacy declarations /* global NETDATA */ @@ -12,7 +12,7 @@ var netdataDashboard = window.netdataDashboard || {}; netdataDashboard.menu = { 'system': { - title: '系统概览', + title: '系统概观', icon: '', info: '一眼掌握系统效能关键指标。' }, @@ -20,46 +20,31 @@ netdataDashboard.menu = { 'services': { title: '系统服务', icon: '', - info: '系统服务的使用情况。 '+ - 'netdata 以 CGROUPS 监视所有系统服务。 '+ - 'cgroups ' + - '(the resources accounting used by containers).' + info: '系统服务的使用情况。netdata 以 CGROUPS 监视所有系统服务。' }, 'ap': { - title: 'AP接入点', + title: '接入点', icon: '', - info: '系统上找到的接入点(即AP模式下的无线接口)的指标。' + info: '在系统上找到的接入点(即AP模式下的无线接口)的性能指标.' }, 'tc': { - title: 'Quality服务', + title: '服务质量', icon: '', - info: 'Netdata使用其收集和可视化tc类利用率 ' + - 'Tc-helper插件. ' + - '如果您也使用FireQOS来设置QoS, ' + - 'Netdata会自动收集接口和类名。如果您的QoS配置包含间接费用 ' + - '计算,这里显示的值将包括这些开销(相同的总带宽 ' + - '“网络接口”部分中报告的接口将低于总带宽 ' + - '这里报告了)。与界面相比,QoS数据收集可能略有时差 ' + - '(QoS数据收集使用BASH脚本,因此数据收集的转移几毫秒 ' + - '应该有正当理由)。' + info: 'Netdata收集和可视化tc 使用its的类利用率 ' }, 'net': { title: '网络接口', icon: '', - info: '

运转 网路介面的效能指标。.

'+ - '

Netdata检索读取/proc/net/dev文件和/sys/class/net/目录的数据。

' + info: '网路介面的效能指标。' }, 'Infiniband': { title: 'Infiniband端口', icon: '', - info: '

绩效和例外统计 '+ - 'Infiniband 端口。 '+ - '单个端口和硬件计数器描述可以在 '+ - 'Mellanox知识库.' + info: '

性能和异常统计信息

' }, 'wireless': { @@ -73,9 +58,9 @@ netdataDashboard.menu = { icon: '', info: function (os) { if (os === "linux") - return '系统网络堆栈的指标。这些指标从/proc/net/netstat收集,或将kprobes附加到内核函数,适用于IPv4和IPv6流量,并与内核网络堆栈的操作有关。'; + return 'Metrics for the networking stack of the system. These metrics are collected from /proc/net/netstat or attaching kprobes to kernel functions, apply to both IPv4 and IPv6 traffic and are related to operation of the kernel networking stack.'; else - return '系统网络堆栈的指标。'; + return 'Metrics for the networking stack of the system.'; } }, @@ -83,43 +68,43 @@ netdataDashboard.menu = { title: 'IPv4网路', icon: '', info: 'IPv4效能指标。' + - 'Internet Protocol version 4 (IPv4) 是 ' + - '互联网协议(IP)的第四版。它是基于标准的核心协议之一 ' + - '互联网上的互联网工作方法。IPv4是一种用于数据包交换的无连接协议' + - '网络。它以最佳努力交付模式运作,因为它不保证交付,也不保证交付 ' + - '它确保正确的顺序或避免重复交付。这些方面,包括数据完整性,' + - '由上层传输协议(如传输控制协议(TCP))解决。' + 'Internet Protocol version 4 (IPv4) is ' + + 'the fourth version of the Internet Protocol (IP). It is one of the core protocols of standards-based ' + + 'internetworking methods in the Internet. IPv4 is a connectionless protocol for use on packet-switched ' + + 'networks. It operates on a best effort delivery model, in that it does not guarantee delivery, nor does ' + + 'it assure proper sequencing or avoidance of duplicate delivery. These aspects, including data integrity, ' + + 'are addressed by an upper layer transport protocol, such as the Transmission Control Protocol (TCP).' }, 'ipv6': { title: 'IPv6网路', icon: '', - info: 'IPv6效能指标。 Internet Protocol version 6 (IPv6) 是互联网协议(IP)的最新版本,该通信协议为网络上的计算机和跨互联网的路由流量提供识别和定位系统。IPv6是由互联网工程特别工作组(IETF)开发的,旨在处理长期预计的IPv4地址用尽问题。IPv6旨在取代IPv4。' + info: 'IPv6效能指标。 Internet Protocol version 6 (IPv6) is the most recent version of the Internet Protocol (IP), the communications protocol that provides an identification and location system for computers on networks and routes traffic across the Internet. IPv6 was developed by the Internet Engineering Task Force (IETF) to deal with the long-anticipated problem of IPv4 address exhaustion. IPv6 is intended to replace IPv4.' }, 'sctp': { title: 'SCTP 网路', icon: '', - info: '

流控传输协议(SCTP) '+ - '是一种计算机网络协议,在传输层运行,其作用类似于流行的 '+ - '协议TCP和UDP。SCTP提供了UDP和TCP的一些功能:它像UDP一样面向消息 '+ - '并确保具有TCP等拥塞控制的消息的可靠、无序传输。 '+ - '它与这些协议不同,它提供了多寻址和冗余路径,以提高弹性和可靠性。

'+ - '

Netdata收集读取/proc/net/sctp/snmp文件的SCTP指标。

' + info: '

Stream Control Transmission Protocol (SCTP) '+ + 'is a computer network protocol which operates at the transport layer and serves a role similar to the popular '+ + 'protocols TCP and UDP. SCTP provides some of the features of both UDP and TCP: it is message-oriented like UDP '+ + 'and ensures reliable, in-sequence transport of messages with congestion control like TCP. '+ + 'It differs from those protocols by providing multi-homing and redundant paths to increase resilience and reliability.

'+ + '

Netdata collects SCTP metrics reading the /proc/net/sctp/snmp file.

' }, 'ipvs': { - title: 'IP 虚拟服务器', + title: 'IP虚拟服务器', icon: '', info: '

IPVS (IP Virtual Server) '+ - '在Linux内核内实现传输层负载平衡,即所谓的第4层切换。 '+ - '在主机上运行的IPVS在一组真实服务器的前部充当负载平衡器, '+ - '它可以将基于TCP/UDP的服务请求定向到真正的服务器, '+ - '并使真实服务器的服务在单个IP地址上显示为虚拟服务。

'+ - '

Netdata收集摘要统计数据,阅读/proc/net/ip_vs_stats。 '+ - '要显示服务及其服务器的统计信息,请运行ipvsadm -Ln --stats '+ - '或ipvsadm -Ln --rate用于费率统计。'+ - '有关详细信息,请参阅 ipvsadm(8).

' + 'implements transport-layer load balancing inside the Linux kernel, so called Layer-4 switching. '+ + 'IPVS running on a host acts as a load balancer at the front of a cluster of real servers, '+ + 'it can direct requests for TCP/UDP based services to the real servers, '+ + 'and makes services of the real servers to appear as a virtual service on a single IP address.

'+ + '

Netdata collects summary statistics, reading /proc/net/ip_vs_stats. '+ + 'To display the statistics information of services and their servers, run ipvsadm -Ln --stats '+ + 'or ipvsadm -Ln --rate for the rate statistics. '+ + 'For details, see ipvsadm(8).

' }, 'netfilter': { @@ -137,7 +122,7 @@ netdataDashboard.menu = { 'cpu': { title: 'CPUs', icon: '', - info: '系统中每一个 CPU 的详细资讯。全部 CPU 的总量可以到 系统概览 区段查看。' + info: '系统中每一个 CPU 的详细资讯。全部 CPU 的总量可以到 系统概观 区段查看。' }, 'mem': { @@ -149,117 +134,101 @@ netdataDashboard.menu = { 'disk': { title: '磁碟', icon: '', - info: '系统中所有磁碟效能资讯图表。特别留意:这是以 iostat -x所取得的效能数据做为呈现。在预设情况下,netdata 不会显示单一分割区与未挂载的虚拟磁碟效能图表。若仍想要显示,可以修改 netdata 设定档中的相关设定。' + info: '系统中所有磁碟效能资讯图表。特别留意:这是以 iostat -x 所取得的效能数据做为呈现。在预设情况下,netdata 不会显示单一分割区与未挂载的虚拟磁碟效能图表。若仍想要显示,可以修改 netdata 设定档中的相关设定。' }, 'mount': { - title: 'Mount Points', + title: '装载点', icon: '', info: '' }, 'mdstat': { - title: 'MD arrays', + title: 'MD阵列', icon: '', - info: '

RAID 设备是由两个或更多真实块设备创建的虚拟设备。 '+ - 'Linux软件RAID设备是 '+ - '通过md(多设备)设备驱动程序实现。

'+ - '

Netdata监控MD数组的当前状态,读取/proc/mdstat和 '+ - '/sys/block/%s/md/mismatch_cnt 档案

' + info: '

RAID设备是由两个或多个实块设备创建的虚拟设备。

' }, 'sensors': { - title: '感测器', + title: '传感器', icon: '', - info: '系统已配置相关感测器的读数。' + info: '配置的系统传感器读数.' }, 'ipmi': { title: 'IPMI', icon: '', - info: '智能平台管理接口(IPMI)是一套自主计算机子系统的计算机接口规范,独立于主机系统的CPU、固件(BIOS或UEFI)和操作系统提供管理和监控功能。' + info: 'The Intelligent Platform Management Interface (IPMI) is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system\'s CPU, firmware (BIOS or UEFI) and operating system.' }, 'samba': { title: 'Samba', icon: '', - info: '此系统的Samba文件共享操作的绩效指标。Samba是Windows服务的实现,包括Windows SMB协议文件共享。' + info: 'Performance metrics of the Samba file share operations of this system. Samba is a implementation of Windows services, including Windows SMB protocol file shares.' }, 'nfsd': { title: 'NFS服器器', icon: '', - info: '网络文件服务器的绩效指标。 '+ + info: 'Performance metrics of the Network File Server. '+ 'NFS '+ - '是一种分布式文件系统协议,允许客户端计算机上的用户通过网络访问文件, '+ - '就像访问本地存储一样。 '+ - '与许多其他协议一样,NFS基于开放网络计算远程过程调用(ONC RPC)系统。' + 'is a distributed file system protocol, allowing a user on a client computer to access files over a network, '+ + 'much like local storage is accessed. '+ + 'NFS, like many other protocols, builds on the Open Network Computing Remote Procedure Call (ONC RPC) system.' }, 'nfs': { title: 'NFS客户端', icon: '', - info: '绩效指标 '+ - 'NFS '+ - '该系统作为NFS客户端的操作。' + info: '显示本机做为 NFS 客户端的效能指标。' }, 'zfs': { title: 'ZFS文件系统', icon: '', - info: '绩效指标 '+ - 'ZFS ARC and L2ARC. '+ - 'ZFS档案系统的效能指标。以下图表呈现来自 '+ - 'arcstat.py 与 '+ - 'arc_summary.py的效能数据。' + info: 'ZFS档案系统的效能指标。以下图表呈现来自 arcstat.pyarc_summary.py 的效能数据。' }, 'zfspool': { - title: 'ZFS pools', + title: 'ZFS提供', icon: '', - info: 'ZFS的状态。' + info: 'ZFS池的状态。' }, 'btrfs': { title: 'BTRFS文件系统', icon: '', - info: 'BTRFS 档案系统磁碟空间使用指标。' + info: 'BTRFS文件系统的磁盘空间指标。' }, 'apps': { title: '应用程序', icon: '', - info: '每个应用程序的统计数据使用 '+ - 'apps.plugin. '+ - '这个插件会浏览所有流程,并汇总 '+ - 'application groups. '+ - '该插件还计算退出子项的资源。 '+ - '因此,对于shell脚本等进程,报告的值包括命令使用的资源 '+ - '这些脚本在每个时间范围内运行。', + info: '使用收集每个应用程序的统计信息', height: 1.5 }, 'groups': { title: '用户组', icon: '', - info: '每个用户组的统计数据使用 '+ + info: 'Per user group statistics are collected using '+ 'apps.plugin. '+ - '此插件浏览所有流程,并汇总每个用户组的统计数据。 '+ - '该插件还计算退出子项的资源。 '+ - '因此,对于shell脚本等进程,报告的值包括命令使用的资源 '+ - '这些脚本在每个时间范围内运行。', + 'This plugin walks through all processes and aggregates statistics per user group. '+ + 'The plugin also counts the resources of exited children. '+ + '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: '用户', icon: '', - info: '每个用户的统计数据是使用 '+ + info: 'Per user statistics are collected using '+ 'apps.plugin. '+ - '此插件浏览所有流程,并汇总每个用户的统计数据。 '+ - '该插件还计算退出子项的资源。 '+ - '因此,对于shell脚本等进程,报告的值包括命令使用的资源 '+ - '这些脚本在每个时间范围内运行。', + 'This plugin walks through all processes and aggregates statistics per user. '+ + 'The plugin also counts the resources of exited children. '+ + '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 }, @@ -282,7 +251,7 @@ netdataDashboard.menu = { 'cgroup': { title: '', icon: '', - info: '容器资源使用率指标。netdata 从 cgroups (abbreviated from control groups 的缩写)中读取这些资讯,cgroups 是 Linux 核心的一个功能,做限制与计算程序集中的资源使用率 (CPU、记忆体、磁碟 I/O、网路...等等)。cgroupsnamespaces (程序之间的隔离) 结合提供了我们所说的:容器。' + info: '容器资源使用率指标。netdata 从 cgroups (control groups 的缩写) 中读取这些资讯,cgroups 是 Linux 核心的一个功能,做限制与计算程序集中的资源使用率 (CPU、记忆体、磁碟 I/O、网路...等等)。cgroupsnamespaces (程序之间的隔离) 结合提供了我们所说的:容器。' }, 'cgqemu': { @@ -294,98 +263,98 @@ netdataDashboard.menu = { 'fping': { title: 'fping', icon: '', - info: '网络延迟统计,通过fpingfping是一个向网络主机发送ICMP回声探针的程序,类似于ping,但在ping多个主机时性能要好得多。3.15之后的fping版本可以直接用作netdata插件。' + info: 'Network latency statistics, via fping. fping is a program to send ICMP echo probes to network hosts, similar to ping, but much better performing when pinging multiple hosts. fping versions after 3.15 can be directly used as netdata plugins.' }, 'gearman': { title: 'Gearman', icon: '', - info: 'Gearman是一个工作服务器,允许您并行工作,加载平衡处理,并在语言之间调用函数。' + info: 'Gearman is a job server that allows you to do work in parallel, to load balance processing, and to call functions between languages.' }, 'ioping': { title: 'ioping', icon: '', - info: '磁盘延迟统计,通过iopingioping是一个从/到磁盘读取/写入数据探针的程序。' + info: 'Disk latency statistics, via ioping. ioping is a program to read/write data probes from/to a disk.' }, 'httpcheck': { title: 'Http Check', icon: '', - info: '使用HTTP检查进行Web服务可用性和延迟监控。此插件是端口检查插件的专用版本。' + info: 'Web Service availability and latency monitoring using HTTP checks. This plugin is a specialized version of the port check plugin.' }, 'memcached': { title: 'memcached', icon: '', - info: 'memcached的绩效指标。Memcached是一个通用的分布式内存缓存系统。它通常用于通过在RAM中缓存数据和对象来加快动态数据库驱动的网站,以减少外部数据源(如数据库或API)必须读取的次数。' + info: 'Performance metrics for memcached. Memcached is a general-purpose distributed memory caching system. It is often used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source (such as a database or API) must be read.' }, 'monit': { title: 'monit', icon: '', - info: 'monit中的检查状态。Monit是一个用于管理和监控Unix系统上的流程、程序、文件、目录和文件系统的实用工具。Monit进行自动维护和维修,并在错误情况下执行有意义的因果行为。' + info: 'Statuses of checks in monit. Monit is a utility for managing and monitoring processes, programs, files, directories and filesystems on a Unix system. Monit conducts automatic maintenance and repair and can execute meaningful causal actions in error situations.' }, 'mysql': { title: 'MySQL', icon: '', - info: '开源关系数据库管理系统(RDBMS)mysql的绩效指标。' + info: 'Performance metrics for mysql, the open-source relational database management system (RDBMS).' }, 'postgres': { title: 'Postgres', icon: '', - info: '对象关系数据库(ORDBMS)PostgresSQL的性能指标。' + info: 'Performance metrics for PostgresSQL, the object-relational database (ORDBMS).' }, 'redis': { title: 'Redis', icon: '', - info: 'redis的绩效指标。Redis(远程字典服务器)是一个实现数据结构服务器的软件项目。它是开源的、联网的、内存的,并存储具有可选耐用性的密钥。' + info: 'Performance metrics for redis. Redis (REmote DIctionary Server) is a software project that implements data structure servers. It is open-source, networked, in-memory, and stores keys with optional durability.' }, 'rethinkdbs': { title: 'RethinkDB', icon: '', - info: 'rethinkdb的绩效指标。RethinkDB是第一个为实时应用程序构建的开源可扩展数据库' + info: 'Performance metrics for rethinkdb. RethinkDB is the first open-source scalable database built for realtime applications' }, 'retroshare': { title: 'RetroShare', icon: '', - info: 'RetroShare的绩效指标。RetroShare是基于基于GNU隐私保护(GPG)的朋友对朋友网络的加密文件共享、无服务器电子邮件、即时消息、在线聊天和BBS的开源软件。' + info: 'Performance metrics for RetroShare. RetroShare is open source software for encrypted filesharing, serverless email, instant messaging, online chat, and BBS, based on a friend-to-friend network built on GNU Privacy Guard (GPG).' }, 'riakkv': { title: 'Riak KV', icon: '', - info: 'Riak KV的指标,分布式键值存储。' + info: 'Metrics for Riak KV, the distributed key-value store.' }, 'ipfs': { title: 'IPFS', icon: '', - info: 'InterPlanetary File System(IPFS)的绩效指标,IPFS是一种内容可寻址的点对点超媒体分发协议。' + info: 'Performance metrics for the InterPlanetary File System (IPFS), a content-addressable, peer-to-peer hypermedia distribution protocol.' }, 'phpfpm': { title: 'PHP-FPM', icon: '', - info: 'PHP-FPM的绩效指标,PHP的替代FastCGI实现。' + info: 'Performance metrics for PHP-FPM, an alternative FastCGI implementation for PHP.' }, 'pihole': { title: 'Pi-hole', icon: '', - info: 'Pi-hole的指标,一个互联网广告的黑洞。' + - ' Pi-Hole API返回的指标都来自过去24小时。' + info: 'Metrics for Pi-hole, a black hole for Internet advertisements.' + + ' The metrics returned by Pi-Hole API is all from the last 24 hours.' }, 'portcheck': { - title: '端口检查', + title: 'Port Check', icon: '', - info: '使用端口检查来监控服务可用性和延迟。' + info: 'Service availability and latency monitoring using port checks.' }, 'postfix': { @@ -427,7 +396,7 @@ netdataDashboard.menu = { 'web_log': { title: undefined, icon: '', - info: '从服务器日志文件中提取的信息。web_log插件逐步解析服务器日志文件,以实时提供关键服务器性能指标的细分。对于Web服务器,可以选择使用扩展日志文件格式(对于nginxapache),为请求和响应提供计时信息和带宽。web_log插件也可以配置为按URL模式提供请求的细分(检查/etc/netdata/python.d/web_log.conf)。' + info: 'Information extracted from a server log file. web_log plugin incrementally parses the server log file to provide, in real-time, a break down of key server performance metrics. For web servers, an extended log file format may optionally be used (for nginx and apache) offering timing information and bandwidth for both requests and responses. web_log plugin may also be configured to provide a break down of requests per URL pattern (check /etc/netdata/python.d/web_log.conf).' }, 'named': { @@ -481,47 +450,47 @@ netdataDashboard.menu = { 'go_expvar': { title: 'Go - expvars', icon: '', - info: 'expvar软件包 公开的运行Go应用程序的统计数据。' + info: 'Statistics about running Go applications exposed by the expvar package.' }, 'chrony': { icon: '', - info: '关于系统时钟性能的计时参数。' + info: 'chronyd parameters about the system’s clock performance.' }, 'couchdb': { icon: '', - info: 'CouchDB的性能指标,该数据库是基于JSON文档的开源数据库,具有HTTP API和多主复制。' + info: 'Performance metrics for CouchDB, the open-source, JSON document-based database with an HTTP API and multi-master replication.' }, 'beanstalk': { title: 'Beanstalkd', icon: '', - info: '使用从beanstalkc提取的数据提供有关beanstalkd服务器和该服务器上可用的任何管道的统计数据' + info: 'Provides statistics on the beanstalkd server and any tubes available on that server using data pulled from beanstalkc' }, 'rabbitmq': { title: 'RabbitMQ', icon: '', - info: 'RabbitMQ开源消息代理的性能数据。' + info: 'Performance data for the RabbitMQ open-source message broker.' }, 'ceph': { title: 'Ceph', icon: '', - info: '提供ceph集群服务器的统计数据,开源分布式存储系统。' + info: 'Provides statistics on the ceph cluster server, the open-source distributed storage system.' }, 'ntpd': { title: 'ntpd', icon: '', - info: '提供网络时间协议守护程序ntpd的内部变量的统计信息,并可选包括配置的对等变量(如果在模块配置中启用)。本模块介绍了ntpq(标准NTP查询程序)所示的绩效指标,使用NTP模式6个UDP数据包与NTP服务器通信。' + info: 'Provides statistics for the internal variables of the Network Time Protocol daemon ntpd and optional including the configured peers (if enabled in the module configuration). The module presents the performance metrics as shown by ntpq (the standard NTP query program) using NTP mode 6 UDP packets to communicate with the NTP server.' }, 'spigotmc': { title: 'Spigot MC', icon: '', - info: '为Spigot Minecraft服务器提供基本性能统计信息。' + info: 'Provides basic performance statistics for the Spigot Minecraft server.' }, 'unbound': { @@ -533,13 +502,13 @@ netdataDashboard.menu = { 'boinc': { title: 'BOINC', icon: '', - info: '为BOINC分布式计算客户端提供任务计数。' + info: 'Provides task counts for BOINC distributed computing clients.' }, 'w1sensor': { title: '1-Wire Sensors', icon: '', - info: '来自1-Wire传感器的数据。目前会自动检测到温度传感器。' + info: 'Data derived from 1-Wire sensors. Currently temperature sensors are automatically detected.' }, 'logind': { @@ -549,21 +518,21 @@ netdataDashboard.menu = { }, 'powersupply': { - title: '电源', + title: 'Power Supply', icon: '', - info: '各种系统电源的统计数据。从Linux电源类收集的数据。' + info: 'Statistics for the various system power supplies. Data collected from Linux power supply class.' }, 'xenstat': { title: 'Xen Node', icon: '', - info: 'Xen节点的一般统计信息。使用xenstat库收集的数据。' + info: 'General statistics for the Xen node. Data collected using xenstat library.' }, 'xendomain': { title: '', icon: '', - info: 'Xen域资源利用率指标。Netdata使用xenstat库读取此信息,该库允许访问虚拟机的资源使用信息(CPU、内存、磁盘I/O、网络)。' + info: 'Xen domain resource utilization metrics. Netdata reads this information using xenstat library which gives access to the resource usage information (CPU, memory, disk I/O, network) for a virtual machine.' }, 'wmi': { @@ -575,43 +544,43 @@ netdataDashboard.menu = { 'perf': { title: 'Perf Counters', icon: '', - info: '性能监控计数器(PMC)。使用使用硬件性能监控单元(PMU)的perf_event_open()系统调用收集的数据。' + info: 'Performance Monitoring Counters (PMC). Data collected using perf_event_open() system call which utilises Hardware Performance Monitoring Units (PMU).' }, 'vsphere': { title: 'vSphere', icon: '', - info: 'ESXI主机和虚拟机的性能统计。使用govmomi库从VMware vCenter Server收集的数据。' + info: 'Performance statistics for ESXI hosts and virtual machines. Data collected from VMware vCenter Server using govmomi library.' }, 'vcsa': { title: 'VCSA', icon: '', - info: 'vCenter Server设备运行状况统计。从健康API收集的数据。' + info: 'vCenter Server Appliance health statistics. Data collected from Health API.' }, 'zookeeper': { title: 'Zookeeper', icon: '', - info: '提供Zookeeper服务器的健康统计数据。使用mntr命令通过命令端口收集的数据。' + info: 'Provides health statistics for Zookeeper server. Data collected through the command port using mntr command.' }, 'hdfs': { title: 'HDFS', icon: '', - info: '提供Hadoop分布式文件系统性能统计信息。模块通过HDFS守护进程的Web界面收集Java管理扩展上的指标。' + info: 'Provides Hadoop Distributed File System performance statistics. Module collects metrics over Java Management Extensions through the web interface of an HDFS daemon.' }, 'am2320': { title: 'AM2320 Sensor', icon: '', - info: '外部AM2320传感器的读数。' + info: 'Readings from the external AM2320 Sensor.' }, 'scaleio': { title: 'ScaleIO', icon: '', - info: 'ScaleIO各个组件的性能和健康统计。通过VxFlex OS Gateway REST API收集的数据。' + info: 'Performance and health statistics for various ScaleIO components. Data collected via VxFlex OS Gateway REST API.' }, 'squidlog': { @@ -623,13 +592,13 @@ netdataDashboard.menu = { 'cockroachdb': { title: 'CockroachDB', icon: '', - info: '各种CockroachDB组件的性能和健康状况统计。' + info: 'Performance and health statistics for various CockroachDB components.' }, 'ebpf': { title: 'eBPF', icon: '', - info: '使用eBPF监控系统调用、内部函数、字节读取、字节写入和错误。' + info: 'Monitor system calls, internal functions, bytes read, bytes written and errors using eBPF.' }, 'filesystem': { @@ -640,74 +609,74 @@ netdataDashboard.menu = { 'vernemq': { title: 'VerneMQ', icon: '', - info: 'VerneMQ开源MQTT经纪人的性能数据。' + info: 'Performance data for the VerneMQ open-source MQTT broker.' }, 'pulsar': { title: 'Pulsar', icon: '', - info: 'Apache Pulsarpub-sub消息系统的摘要、命名空间和主题性能数据。' + info: 'Summary, namespaces and topics performance data for the Apache Pulsar pub-sub messaging system.' }, 'anomalies': { title: 'Anomalies', icon: '', - info: '与关键系统指标相关的异常分数。高异常概率表示奇怪的行为,并可能触发经过训练的模型的异常预测。有关更多详细信息,请阅读异常收集器文档。' + info: 'Anomaly scores relating to key system metrics. A high anomaly probability indicates strange behaviour and may trigger an anomaly prediction from the trained models. Read the anomalies collector docs for more details.' }, 'alarms': { title: 'Alarms', icon: '', - info: '显示警报随时间推移状态的图表。更多详细信息此处。' + info: 'Charts showing alarm status over time. More details here.' }, 'statsd': { title: 'StatsD', icon: '', - info:'StatsD是一个行业标准技术堆栈,用于监控应用程序和检测任何软件以提供自定义指标。Netdata允许用户在不同图表中组织指标,并轻松可视化任何应用程序指标。在Netdata Learn上阅读更多信息。' + info:'StatsD is an industry-standard technology stack for monitoring applications and instrumenting any piece of software to deliver custom metrics. Netdata allows the user to organize the metrics in different charts and visualize any application metric easily. Read more on Netdata Learn.' }, 'supervisord': { title: 'Supervisord', icon: '', - info: '主管控制的每组流程的详细统计数据。' + - 'Netdata使用getAllProcessInfo方法收集这些指标。' + info: 'Detailed statistics for each group of processes controlled by Supervisor. ' + + 'Netdata collects these metrics using getAllProcessInfo method.' }, 'systemdunits': { - title: 'systemd units', + title: 'systemd单位', icon: '', - info: 'systemd在11种不同类型的不同实体之间提供了一个依赖系统,称为“单位”。 ' + - '单元封装了与系统启动和维护相关的各种对象。 ' + - '单元可能是活动(表示启动、绑定、插入,具体取决于单元类型), ' + - '或不活跃(意味着停止、未绑定、断开连接), ' + - '以及在激活或停用的过程中,即在两种状态之间(这些状态称为激活停用)。 ' + - '特殊的失败状态也可用,这与不活跃非常相似,并在服务以某种方式失败时(进程在退出时、崩溃、操作超时或重新启动过多后返回错误代码)时输入。 ' + - '有关详细信息,请参阅 systemd(1)。' + info: 'systemd provides a dependency system between various entities called "units" of 11 different types. ' + + 'Units encapsulate various objects that are relevant for system boot-up and maintenance. ' + + 'Units may be active (meaning started, bound, plugged in, depending on the unit type), ' + + 'or inactive (meaning stopped, unbound, unplugged), ' + + 'as well as in the process of being activated or deactivated, i.e. between the two states (these states are called activating, deactivating). ' + + 'A special failed state is available as well, which is very similar to inactive and is entered when the service failed in some way (process returned error code on exit, or crashed, an operation timed out, or after too many restarts). ' + + 'For details, see systemd(1).' }, 'changefinder': { title: 'ChangeFinder', icon: '', - info: '使用机器学习在线更改点检测。更多详细信息此处。' + info: 'Online changepoint detection using machine learning. More details here.' }, 'zscores': { title: 'Z-Scores', icon: '', - info: 'Z scores与关键系统指标相关的分数。' + info: 'Z scores scores relating to key system metrics.' }, 'anomaly_detection': { title: 'Anomaly Detection', icon: '', - info: '与异常检测、异常尺寸增加或高于通常异常率相关的图表可能是一些异常行为的迹象。有关更多详细信息,请阅读我们的异常检测指南。' + info: 'Charts relating to anomaly detection, increased anomalous dimensions or a higher than usual anomaly_rate could be signs of some abnormal behaviour. Read our anomaly detection guide for more details.' }, 'fail2ban': { title: 'Fail2ban', icon: '', - info: 'Netdata通过读取Fail2ban日志文件来跟踪当前的监狱状态。' + info: 'Netdata keeps track of the current jail status by reading the Fail2ban log file.' }, }; @@ -721,22 +690,22 @@ netdataDashboard.menu = { netdataDashboard.submenu = { 'web_log.squid_bandwidth': { title: '频宽', - info: 'squid响应的带宽(发送)。此图表可能会出现异常的峰值,因为带宽是在服务器保存日志行时核算的,即使服务日志行所需的时间跨度更长。我们建议使用QoS(例如FireQOS)来准确核算服务器带宽。' + info: 'Bandwidth of responses (sent) by squid. This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the server bandwidth.' }, 'web_log.squid_responses': { title: '回应', - info: '与squid发送的回复相关的信息。' + info: 'Information related to the responses sent by squid.' }, 'web_log.squid_requests': { title: '请求', - info: 'squid收到的与请求相关的信息。' + info: 'Information related to the requests squid has received.' }, 'web_log.squid_hierarchy': { title: '等级制度', - info: '用于服务请求的squid层次结构的绩效指标。' + info: 'Performance metrics for the squid hierarchy used to serve the requests.' }, 'web_log.squid_squid_transport': { @@ -745,12 +714,12 @@ netdataDashboard.submenu = { 'web_log.squid_squid_cache': { title: '缓存', - info: 'squid缓存性能的性能指标。' + info: 'squid缓存性能的性能指标.' }, 'web_log.squid_timings': { title: 'timings', - info: 'squid请求的持续时间。可能会报告不切实际的激增,因为squid会在请求完成后记录请求的总时间。特别是对于HTTPS,客户端从代理获取隧道,并直接与上游服务器交换请求,因此squid无法评估单个请求并报告隧道打开的总时间。' + info: 'Duration of squid requests. Unrealistic spikes may be reported, since squid logs the total time of the requests, when they complete. Especially for HTTPS, the clients get a tunnel from the proxy and exchange requests directly with the upstream servers, so squid cannot evaluate the individual requests and reports the total time the tunnel was open.' }, 'web_log.squid_clients': { @@ -758,32 +727,28 @@ netdataDashboard.submenu = { }, 'web_log.bandwidth': { - info: '请求(接收)和响应(发送)的带宽。接收需要扩展日志格式(没有它,Web服务器日志没有此信息)。此图表可能会出现异常的峰值,因为带宽是在Web服务器保存日志行时核算的,即使服务日志行所需的时间跨度更长。我们建议使用QoS(例如FireQOS)来准确核算Web服务器带宽。' + info: 'Bandwidth of requests (received) and responses (sent). received requires an extended log format (without it, the web server log does not have this information). This chart may present unusual spikes, since the bandwidth is accounted at the time the log line is saved by the web server, even if the time needed to serve it spans across a longer duration. We suggest to use QoS (e.g. FireQOS) for accurate accounting of the web server bandwidth.' }, 'web_log.urls': { - info: '/etc/netdata/python.d/web_log.conf中定义的每个URL模式的请求数量。该图表计算与定义的URL模式匹配的所有请求,独立于Web服务器响应代码(即成功和失败)。' + info: 'Number of requests for each URL pattern defined in /etc/netdata/python.d/web_log.conf. This chart counts all requests matching the URL patterns defined, independently of the web server response codes (i.e. both successful and unsuccessful).' }, 'web_log.clients': { - info: '显示访问Web服务器的唯一客户端IP数量的图表。' + info: 'Charts showing the number of unique client IPs, accessing the web server.' }, 'web_log.timings': { - info: 'Web服务器响应时间-Web服务器准备和响应请求所需的时间。这需要扩展日志格式,其含义特定于Web服务器。对于大多数Web服务器来说,这计入了从收到完整请求到发送响应最后一个字节的时间。因此,它包括响应的网络延迟,但它不包括请求的网络延迟。' + info: 'Web server response timings - the time the web server needed to prepare and respond to requests. This requires an extended log format and its meaning is web server specific. For most web servers this accounts the time from the reception of a complete request, to the dispatch of the last byte of the response. So, it includes the network delays of responses, but it does not include the network delays of requests.' }, 'mem.ksm': { title: 'deduper (ksm)', - info: 'Kernel同页合并 '+ - '(KSM)性能监控,从/sys/kernel/mm/ksm/中的几个文件中读取。 '+ - 'KSM是Linux内核中节省内存的重复数据删除功能。 '+ - 'KSM守护进程ksmd定期扫描已注册的用户内存区域, '+ - '寻找内容相同的页面,这些页面可以替换为单个受写保护的页面。' + info: 'Kernel Same-page Merging (KSM) 效能监视,经由读取 /sys/kernel/mm/ksm/ 下的档案而来。KSM 是在 Linux 核心 (自 2.6.32 版起) 内含的一种节省记忆体使用率重复资料删除功能。)。 KSM 服务程序 ksmd 会定期扫描记忆体区域,寻找正有资料要更新进来且相同资料存在的分页。KSM 最初是从 KVM 专案开发中而来,利用这种共用相同资料的机制,即可以让更多的虚拟机器放到记忆体中。另外,对许多会产生同样内容的应用程序来说,这个功能是相当有效益的。' }, 'mem.hugepages': { - info: 'Hugepages是一项功能,允许内核利用现代硬件架构的多个页面大小功能。内核创建了多页虚拟内存,从物理RAM和交换进行映射。CPU架构中有一个名为“翻译Lookaside缓冲区”(TLB)的机制,用于管理虚拟内存页面与实际物理内存地址的映射。TLB是一个有限的硬件资源,因此使用默认页面大小的大量物理内存会消耗TLB并增加处理开销。通过使用大型页面,内核能够创建更大大小的页面,每个页面消耗TLB中的单个资源。大型页面被固定在物理RAM上,无法交换/分页。' + info: 'Hugepages is a feature that allows the kernel to utilize the multiple page size capabilities of modern hardware architectures. The kernel creates multiple pages of virtual memory, mapped from both physical RAM and swap. There is a mechanism in the CPU architecture called "Translation Lookaside Buffers" (TLB) to manage the mapping of virtual memory pages to actual physical memory addresses. The TLB is a limited hardware resource, so utilizing a large amount of physical memory with the default page size consumes the TLB and adds processing overhead. By utilizing Huge Pages, the kernel is able to create pages of much larger sizes, each page consuming a single resource in the TLB. Huge Pages are pinned to physical RAM and cannot be swapped/paged out.' }, 'mem.numa': { @@ -791,300 +756,300 @@ netdataDashboard.submenu = { }, 'mem.ecc': { - info: '

ECC内存'+ - '是一种使用错误更正代码(ECC)进行检测的计算机数据存储 '+ - '并纠正内存中发生的n位数据损坏。 '+ - '通常,ECC内存保持对单位错误的免疫记忆系统: '+ - '从每个单词读取的数据始终与写入它的数据相同, '+ - '即使实际存储的位数之一被翻转到错误的状态。

'+ - '

内存错误可分为两类:'+ - '软错误,随机损坏位数,但不留下物理损坏。 '+ - '软错误本质上是短暂的,不可重复,可能是由于电 '+ - '磁干扰。 '+ - '硬错误,它以可重复的方式损坏位,因为 '+ - '物理/硬件缺陷或环境问题。' + info: '

ECC 内存 '+ + 'is a type of computer data storage that uses an error correction code (ECC) to detect '+ + 'and correct n-bit data corruption which occurs in memory. '+ + 'Typically, ECC memory maintains a memory system immune to single-bit errors: '+ + 'the data that is read from each word is always the same as the data that had been written to it, '+ + 'even if one of the bits actually stored has been flipped to the wrong state.

'+ + '

Memory errors can be classified into two types: '+ + 'Soft errors, which randomly corrupt bits but do not leave physical damage. '+ + 'Soft errors are transient in nature and are not repeatable, can be because of electrical or '+ + 'magnetic interference. '+ + 'Hard errors, which corrupt bits in a repeatable manner because '+ + 'of a physical/hardware defect or an environmental problem.' }, 'mem.pagetype': { - info: '内存统计数据可从 '+ - '记忆分配器。'+ - 'buddy分配器是系统内存分配器。 '+ - '整个内存空间被分割成物理页面,这些页面按 '+ - 'NUMA节点,区域, '+ - '迁移类型,以及块的大小。 '+ - '通过根据页面的移动能力对其进行分组, '+ - '内核可以回收页面块中的页面,以满足高阶分配。 '+ - '当内核或应用程序请求一些内存时,好友分配器会提供与请求最近的页面匹配。' + info: '可用可用内存的统计信息 '+ + '内存伙伴分配器. '+ + 'The buddy allocator is the system memory allocator. '+ + 'The whole memory space is split in physical pages, which are grouped by '+ + 'NUMA node, zone, '+ + 'migrate type, and size of the block. '+ + 'By keeping pages grouped based on their ability to move, '+ + 'the kernel can reclaim pages within a page block to satisfy a high-order allocation. '+ + 'When the kernel or an application requests some memory, the buddy allocator provides a page that matches closest the request.' }, 'ip.ecn': { - info: '显式拥堵通知(ECN) '+ - '是IP和TCP的扩展,允许在不丢失数据包的情况下端到端通知网络拥塞。 '+ - 'ECN是一项可选功能,可以在两个支持ECN的端点之间使用,当 '+ - '基础网络基础设施也支持它。' + info: '显式拥塞通知(ECN) '+ + 'is an extension to the IP and to the TCP 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.' }, 'ip.multicast': { - info: 'IP多播是一种技术 '+ - 'IP 网络上的一对多通信。 '+ - '多播高效地使用网络基础设施,要求源只发送一次数据包, '+ - '即使它需要交付给大量接收器。 '+ - '网络中的节点仅在必要时负责复制数据包以到达多个接收器。' + info: 'IP multicast is a technique for '+ + 'IP网络上的一对多通信。'+ + 'Multicast uses network infrastructure efficiently by requiring the source to send a packet only once, '+ + 'even if it needs to be delivered to a large number of receivers. '+ + 'The nodes in the network take care of replicating the packet to reach multiple receivers only when necessary.' }, 'ip.broadcast': { info: '在计算机网络中, '+ - '广播是指传输网络上每台设备都将接收的数据包。 '+ - '在实践中,广播范围仅限于广播领域。' + 'broadcasting refers to transmitting a packet that will be received by every device on the network. '+ + 'In practice, the scope of the broadcast is limited to a broadcast domain.' }, 'netfilter.conntrack': { - title: 'connection tracker', + title: '连接跟踪器', info: 'Netfilter connection tracker 效能指标。Connection tracker 会追踪这台主机上所有的连接,包括流入与流出。工作原理是将所有开启的连接都储存到资料库,以追踪网路、位址转换与连接目标。' }, 'netfilter.nfacct': { - title: 'bandwidth accounting', - info: '以下信息使用nfacct.plugin阅读。' + title: '带宽计费', + info: 'The following information is read using the nfacct.plugin.' }, 'netfilter.synproxy': { - title: 'DDoS保护', - info: 'DDoS保护性能指标。SYNPROXY '+ - '是TCP SYN数据包代理。 '+ - '它用于保护任何TCP服务器(如Web服务器)免受SYN洪水和类似的DDoS攻击。 '+ - 'SYNPROXY拦截新的TCP连接,并使用syncookie处理最初的3向握手 '+ - '而不是连接来建立连接。 '+ - '它经过优化,可以利用所有可用的CPUs处理数百万个数据包,而无需 '+ - '连接之间的任何并发锁定。 '+ - '它可用于任何类型的TCP流量(甚至加密), '+ - '因为它不会干扰内容本身。' + title: 'DDoS防护', + info: 'DDoS保护性能指标. SYNPROXY '+ + '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. '+ + 'SYNPROXY intercepts new TCP connections and handles the initial 3-way handshake using syncookies '+ + 'instead of conntrack to establish the connection. '+ + '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.' }, 'ipfw.dynamic_rules': { - title: 'dynamic rules', - info: '由相应的有状态防火墙规则创建的动态规则数量。' + title: '动态规则', + info: '由相应的有状态防火墙规则创建的动态规则数。' }, 'system.softnet_stat': { title: 'softnet', info: function (os) { if (os === 'linux') - return '

与网络接收工作相关的CPU SoftIRQ的统计数据。 '+ - '每个CPU内核的细分可以在CPU/softnet统计上找到。 '+ - '有关识别网络驱动程序相关问题并进行故障诊断的更多信息,请参阅 '+ - '红帽企业Linux网络性能调优指南

'+ - '

已处理 - 处理数据包。 '+ - '已删除 - 由于网络设备积压已满,数据包已丢失。 '+ - '挤压 - 网络设备预算消耗或达到时限的次数, '+ - '但还有更多工作要做。 '+ - 'ReceivedRPS - 这个CPU被唤醒通过处理器间中断处理数据包的次数。 '+ - '流量限制计数 - 达到流量限制的次数(流量限制是可选的 '+ - '接收数据包转向功能)。

'; + return '

与网络接收工作相关的CPU软件的统计信息. '+ + 'Break down per CPU core can be found at CPU / softnet statistics. '+ + 'More information about identifying and troubleshooting network driver related issues can be found at '+ + 'Red Hat Enterprise Linux Network Performance Tuning Guide.

'+ + '

Processed - packets processed. '+ + 'Dropped - packets dropped because the network device backlog was full. '+ + 'Squeezed - number of times the network device budget was consumed or the time limit was reached, '+ + 'but more work was available. '+ + 'ReceivedRPS - number of times this CPU has been woken up to process packets via an Inter-processor Interrupt. '+ + 'FlowLimitCount - number of times the flow limit has been reached (flow limiting is an optional '+ + 'Receive Packet Steering feature).

'; else - return '与网络接收工作相关的CPU SoftIRQ的统计数据。'; + return 'Statistics for CPUs SoftIRQs related to network receive work.'; } }, 'system.clock synchronization': { info: 'NTP '+ - '允许您自动将系统时间与远程服务器同步。 '+ - '这通过与已知具有准确时间的服务器同步来保持机器时间的准确性。' + '允许您自动将系统时间与远程服务器同步. '+ + 'This keeps your machine’s time accurate by syncing with servers that are known to have accurate times.' }, 'cpu.softnet_stat': { - title: 'softnet', + title: '软网', info: function (os) { if (os === 'linux') - return '

与网络接收工作相关的CPU SoftIRQ的统计数据。 '+ - '所有CPU内核的总和可在系统/软网统计中找到。 '+ - '有关识别网络驱动程序相关问题并进行故障诊断的更多信息,请参阅 '+ - '红帽企业Linux网络性能调优指南

'+ - '

已处理 - 处理数据包。 '+ - '已删除 - 由于网络设备积压已满,数据包已丢失。 '+ - '挤压 - 网络设备预算消耗或达到时限的次数, '+ - '但还有更多工作要做。 '+ - 'ReceivedRPS - 这个CPU被唤醒通过处理器间中断处理数据包的次数。 '+ - '流量限制计数 - 达到流量限制的次数(流量限制是可选的 '+ - '接收数据包转向功能)。

'; + return '

与网络接收工作相关的CPU软件的统计信息. '+ + 'Total for all CPU cores can be found at System / softnet statistics. '+ + 'More information about identifying and troubleshooting network driver related issues can be found at '+ + 'Red Hat Enterprise Linux Network Performance Tuning Guide.

'+ + '

Processed - packets processed. '+ + 'Dropped - packets dropped because the network device backlog was full. '+ + 'Squeezed - number of times the network device budget was consumed or the time limit was reached, '+ + 'but more work was available. '+ + 'ReceivedRPS - number of times this CPU has been woken up to process packets via an Inter-processor Interrupt. '+ + 'FlowLimitCount - number of times the flow limit has been reached (flow limiting is an optional '+ + 'Receive Packet Steering feature).

'; else - return '与网络接收工作相关的每个CPU核心SoftIRQ的统计数据。所有CPU内核的总和可在系统/软网统计中找到。'; + return 'Statistics for per CPUs core SoftIRQs related to network receive work. Total for all CPU cores can be found at System / softnet statistics.'; } }, 'go_expvar.memstats': { - title: 'memory statistics', - info: '运行时内存统计。有关每个图表和值的更多信息,请参阅runtime.MemStats文档。' + title: '内存统计信息', + info: 'Go runtime memory statistics. See runtime.MemStats documentation for more info about each chart and the values.' }, 'couchdb.dbactivity': { - title: 'db activity', - info: '整个数据库为整个服务器读取和写入。这包括任何外部HTTP流量,以及在集群中执行的内部复制流量,以确保节点一致性。' + title: 'db活动', + info: 'Overall database reads and writes for the entire server. This includes any external HTTP traffic, as well as internal replication traffic performed in a cluster to ensure node consistency.' }, 'couchdb.httptraffic': { - title: 'http traffic breakdown', - info: '所有HTTP流量,按请求类型(GETPUTPOST等)和响应状态代码(2002014xx等)

此处的任何5xx错误都表示可能存在CouchDB错误;请查看日志文件以了解更多信息。' + title: 'http流量细分', + info: 'All HTTP traffic, broken down by type of request (GET, PUT, POST, etc.) and response status code (200, 201, 4xx, etc.)

Any 5xx errors here indicate a likely CouchDB bug; check the logfile for further information.' }, 'couchdb.ops': { - title: 'server operations' + title: '服务器操作' }, 'couchdb.perdbstats': { - title: 'per db statistics', - info: '每个数据库的统计数据。这包括每个数据库3个大小的图表:活动(数据库中实时数据的大小)、外部(数据库内容的未压缩大小)和文件(磁盘上文件的大小,不包括任何视图和索引)。它还包括每个数据库的文件数量和删除的文件数量。' + title: '每db统计信息', + info: 'Statistics per database. This includes 3 size graphs per database: active (the size of live data in the database), external (the uncompressed size of the database contents), and file (the size of the file on disk, exclusive of any views and indexes). It also includes the number of documents and number of deleted documents per database.' }, 'couchdb.erlang': { - title: 'erlang statistics', - info: '有关托管CouchDB的Erlang VM状态的详细信息。这些仅适用于高级用户。峰值消息队列的高值(>10e6)通常表示重载条件。' + title: 'erlang统计信息', + info: 'Detailed information about the status of the Erlang VM that hosts CouchDB. These are intended for advanced users only. High values of the peak message queue (>10e6) generally indicate an overload condition.' }, 'ntpd.system': { - title: 'system', - info: '阅读列表广告牌ntpq -c rl所示的系统变量统计信息。系统变量被分配为零的关联ID,也可以显示在readvar广告牌ntpq -c“rv 0”中。这些变量用于时钟纪律算法,以计算最低和最稳定的偏移量。' + title: '系统', + info: 'Statistics of the system variables as shown by the readlist billboard ntpq -c rl. System variables are assigned an association ID of zero and can also be shown in the readvar billboard ntpq -c "rv 0". These variables are used in the Clock Discipline Algorithm, to calculate the lowest and most stable offset.' }, 'ntpd.peers': { - title: 'peers', - info: '在/etc/ntp.conf中配置的每个对等变量的统计信息,如readvar广告牌ntpq -c“rv <association>”所示,而每个对等方都分配了一个非零关联ID,如ntpq -c“apeers”所示。该模块定期扫描新的/更改的对等机(默认:每60秒一次)。ntpd从可用对等机中选择最佳对等机来同步时钟。至少需要3名同行才能正确识别最佳同行。' + title: '同行', + info: 'Statistics of the peer variables for each peer configured in /etc/ntp.conf as shown by the readvar billboard ntpq -c "rv <association>", while each peer is assigned a nonzero association ID as shown by ntpq -c "apeers". The module periodically scans for new/changed peers (default: every 60s). ntpd selects the best possible peer from the available peers to synchronize the clock. A minimum of at least 3 peers is required to properly identify the best possible peer.' }, 'mem.page_cache': { - title: 'page cache (eBPF)', - info: '监控对用于操作Linux页面缓存的函数的调用。当与应用程序的集成启用时,Netdata还根据应用程序显示页面缓存操作。' + title: '页面缓存(eBPF)', + info: 'Monitor calls to functions used to manipulate Linux page cache. When integration with apps is enabled, Netdata also shows page cache manipulation per application.' }, 'apps.page_cache': { - title: 'page cache (eBPF)', - info: 'Netdata还在内存子菜单中对这些图表进行了摘要。' + title: '页面缓存 (eBPF)', + info: 'Netdata also gives a summary for these charts in Memory submenu.' }, 'filesystem.vfs': { title: 'vfs (eBPF)', - info: '监控对用于操作文件系统的调用。当与应用程序的集成启用时,Netdata还根据应用程序显示虚拟文件系统。' + info: 'Monitor calls to functions used to manipulate File Systems. When integration with apps is enabled, Netdata also shows Virtual File System per application.' }, 'apps.vfs': { title: 'vfs (eBPF)', - info: 'Netdata还在文件系统子菜单中对这些图表进行了摘要。' + info: 'Netdata also gives a summary for these charts in Filesystem submenu.' }, 'filesystem.ext4_latency': { - title: 'ext4 latency (eBPF)', - info: '延迟是完成事件所需的时间。我们计算调用和返回时间之间的差异,这跨越磁盘I/O、文件系统操作(锁定、I/O)、运行队列延迟以及与监控操作相关的所有事件。基于BCC工具中的eBPF ext4dist。' + title: 'ext4延迟 (eBPF)', + info: 'Latency is the time it takes for an event to be completed. We calculate the difference between the calling and return times, this spans disk I/O, file system operations (lock, I/O), run queue latency and all events related to the monitored action. Based on the eBPF ext4dist from BCC tools.' }, 'filesystem.xfs_latency': { - title: 'xfs latency (eBPF)', - info: '延迟是完成事件所需的时间。我们计算调用和返回时间之间的差异,这跨越磁盘I/O、文件系统操作(锁定、I/O)、运行队列延迟以及与监控操作相关的所有事件。基于BCC工具中的eBPF xfsdist。' + title: 'xfs延迟 (eBPF)', + info: 'Latency is the time it takes for an event to be completed. We calculate the difference between the calling and return times, this spans disk I/O, file system operations (lock, I/O), run queue latency and all events related to the monitored action. Based on the eBPF xfsdist from BCC tools.' }, 'filesystem.nfs_latency': { - title: 'nfs latency (eBPF)', - info: '延迟是完成事件所需的时间。我们计算调用和返回时间之间的差异,这跨越磁盘I/O、文件系统操作(锁定、I/O)、运行队列延迟以及与监控操作相关的所有事件。基于BCC工具中的eBPF nfsdist。' + title: 'nfs延迟 (eBPF)', + info: 'Latency is the time it takes for an event to be completed. We calculate the difference between the calling and return times, this spans disk I/O, file system operations (lock, I/O), run queue latency and all events related to the monitored action. Based on the eBPF nfsdist from BCC tools.' }, 'filesystem.zfs_latency': { - title: 'zfs latency (eBPF)', - info: '延迟是完成事件所需的时间。我们计算调用和返回时间之间的差异,这跨越磁盘I/O、文件系统操作(锁定、I/O)、运行队列延迟以及与监控操作相关的所有事件。基于BCC工具中的eBPF zfsdist。' + title: 'zfs延迟 (eBPF)', + info: 'Latency is the time it takes for an event to be completed. We calculate the difference between the calling and return times, this spans disk I/O, file system operations (lock, I/O), run queue latency and all events related to the monitored action. Based on the eBPF zfsdist from BCC tools.' }, 'filesystem.btrfs_latency': { - title: 'btrfs latency (eBPF)', - info: '延迟是完成事件所需的时间。我们计算调用和返回时间之间的差异,获得最终结果的对数,并将一个值相加到各自的bin。基于BCC工具中的eBPF btrfsdist。' + title: 'btrfs延迟 (eBPF)', + info: 'Latency is the time it takes for an event to be completed. We calculate the difference between the calling and return times, we get the logarithmic for the final result and we sum one value to the respective bin. Based on the eBPF btrfsdist from BCC tools.' }, 'filesystem.file_access': { - title: 'file access (eBPF)', - info: '当与应用程序的集成启用时,Netdata还根据应用程序显示文件访问权限。' + title: '文件访问权限 (eBPF)', + info: 'When integration with apps is enabled, Netdata also shows file access per application.' }, 'apps.file_access': { - title: 'file access (eBPF)', - info: 'Netdata还在文件系统子菜单上提供了此图表的摘要(有关eBPF插件文件图表部分的更多详细信息)。' + title: '文件访问权限 (eBPF)', + info: 'Netdata also gives a summary for this chart on Filesystem submenu (more details on eBPF plugin file chart section).' }, 'ip.kernel': { - title: 'kernel functions (eBPF)', - info: '当ebpf.plugin在主机上运行时,会制作下一个图表。当与应用程序的集成启用时,Netdata还根据应用程序显示对内核函数的调用。' + title: '内核函数 (eBPF)', + info: 'Next charts are made when ebpf.plugin is running on your host. When integration with apps is enabled, Netdata also shows calls for kernel functions per application.' }, 'apps.net': { - title: 'network', - info: 'Netdata还总结了网络堆栈子菜单中的eBPF图表。' + title: '网络', + info: 'Netdata also gives a summary for eBPF charts in Networking Stack submenu.' }, 'system.ipc semaphores': { - info: '系统V信号量是一种进程间通信(IPC)机制。 '+ + info: 'System V信号量是一种进程间通信(IPC)机制. '+ 'It allows processes or threads within a process to synchronize their actions. '+ - '它们通常用于监控共享内存段等系统资源的可用性。 ' + - '有关详细信息,请参阅svipc(7)。 ' + - '要查看主机IPC信号量信息,请运行ipcs -us。对于限制,请运行ipcs -ls。' + 'They are often used to monitor and control the availability of system resources such as shared memory segments. ' + + 'For details, see svipc(7). ' + + 'To see the host IPC semaphore information, run ipcs -us. For limits, run ipcs -ls.' }, 'system.ipc shared memory': { - info: '系统共享内存是一种进程间通信(IPC)机制。 '+ - '它允许进程通过共享内存区域来通信信息。 '+ - '这是可用的最快进程间通信形式,因为当数据在进程之间传递时(没有复制),不会发生内核参与。 '+ - '通常,进程必须同步对共享内存对象的访问,例如使用POSIX信号量。 '+ - '有关详细信息,请参阅svipc(7)。 '+ - '要查看主机IPC共享内存信息,请运行ipcs -um。对于限制,请运行ipcs -lm。' + info: 'System V共享内存是一种进程间通信(IPC)机制. '+ + 'It allows processes to communicate information by sharing a region of memory. '+ + 'It is the fastest form of inter-process communication available since no kernel involvement occurs when data is passed between the processes (no copying). '+ + 'Typically, processes must synchronize their access to a shared memory object, using, for example, POSIX semaphores. '+ + 'For details, see svipc(7). '+ + 'To see the host IPC shared memory information, run ipcs -um. For limits, run ipcs -lm.' }, 'system.ipc message queues': { - info: '系统消息队列是一种进程间通信(IPC)机制。 '+ - '它允许进程以消息形式交换数据。 '+ - '有关详细信息,请参阅svipc(7)。 ' + - '要查看主机IPC消息信息,请运行ipcs -uq。对于限制,请运行ipcs -lq。' + info: 'System V消息队列是一种进程间通信(IPC)机制. '+ + 'It allow processes to exchange data in the form of messages. '+ + 'For details, see svipc(7). ' + + 'To see the host IPC messages information, run ipcs -uq. For limits, run ipcs -lq.' }, 'system.interrupts': { - info: 'Interrupts 是'+ - '通过外部设备(通常是I/O设备)或程序(运行进程)发送到CPU。 '+ - '它们告诉CPU停止当前的活动,并执行操作系统的相应部分。 '+ - 'Interrupt 类型包括 '+ - 'hardware (由硬件设备生成,以表明它们需要操作系统的注意), '+ - 'software (当程序想要请求操作系统执行系统调用时生成), 及 '+ - 'traps (由CPU本身生成,以指示发生了某些错误或情况,需要操作系统的帮助).' + info: 'Interrupts are signals '+ + 'sent to the CPU by external devices (normally I/O devices) or programs (running processes). '+ + 'They tell the CPU to stop its current activities and execute the appropriate part of the operating system. '+ + 'Interrupt types are '+ + 'hardware (generated by hardware devices to signal that they need some attention from the OS), '+ + 'software (generated by programs when they want to request a system call to be performed by the operating system), and '+ + 'traps (generated by the CPU itself to indicate that some error or condition occurred for which assistance from the operating system is needed).' }, 'system.softirqs': { - info: '软件中断(或“softirqs”)是内核中最古老的延迟执行机制之一。 '+ - '内核执行的几项任务并不重要: '+ - '如有必要,它们可以被长时间推迟。 '+ - '在启用所有中断的情况下,可以执行可执行的任务 '+ - '(软件在硬件中断后模式化)。 '+ - '将它们从中断处理程序中取出有助于保持内核响应时间小。' + info: '软件中断(或“softirq”)是内核中最古老的延迟执行机制之一. '+ + 'Several tasks among those executed by the kernel are not critical: '+ + 'they can be deferred for a long period of time, if necessary. '+ + 'The deferrable tasks can execute with all interrupts enabled '+ + '(softirqs are patterned after hardware interrupts). '+ + 'Taking them out of the interrupt handler helps keep kernel response time small.' }, 'cpu.softirqs': { - info: '每个CPU的软件中断总数。 '+ - '要查看系统的总数,请检查 softirqs 查看' + info: '每个CPU的软件中断总数. '+ + 'To see the total number for the system check the softirqs section.' }, 'cpu.interrupts': { - info: '每个CPU的中断总数。 '+ - '要查看系统的总数,请查看中断部分。 '+ - '/proc/interrupts的最后一列提供了中断描述或注册该中断处理程序的设备名称。' + info: '每个CPU的中断总数. '+ + 'To see the total number for the system check the interrupts section. '+ + 'The last column in /proc/interrupts provides an interrupt description or the device name that registered the handler for that interrupt.' }, 'cpu.throttling': { - info: ' CPU节流通常用于自动减慢计算机的速度 '+ - '在可能的情况下减少能源消耗并节省电池电量。' + info: ' CPU调节通常用于自动降低计算机速度'+ + 'when possible to use less energy and conserve battery.' }, 'cpu.cpuidle': { - info: '空闲状态(C-states) '+ - '用于在处理器闲置时节省电力。' + info: 'Idle States (C-states) '+ + 'are used to save power when the processor is idle.' }, 'services.net': { - title: 'network (eBPF)', + title: '网络 (eBPF)', }, 'services.page_cache': { - title: 'pache cache (eBPF)', + title: 'Pache缓存 (eBPF)', }, }; @@ -1107,17 +1072,10 @@ var cgroupMemLimitIsSet = 0; netdataDashboard.context = { 'system.cpu': { info: function (os) { - void (os); - return 'CPU 使用率总表 (全部核心)。 当数值为 100% 时,表示您的 CPU 非常忙碌没有闲置空间。您可以在 CPUs 区段及以及 应用程序 区段深入了解每个核心与应用程序的使用情况。' + void(os); + return 'CPU 使用率总表 (全部核心)。 当数值为 100% 时,表示您的 CPU 非常忙碌没有闲置空间。您可以在 CPU 区段及以及 应用程序 区段深入了解每个核心与应用程序的使用情况。' + netdataDashboard.sparkline('
请特别关注 iowait ', 'system.cpu', 'iowait', '%', ',如果它一直处于较高的情况,这表示您的磁碟是效能瓶颈,您的系统效能会明显降低。') - + netdataDashboard.sparkline( - '
另一个重要的指标是 softirq ', - 'system.cpu', - 'softirq', - '%', - ',若这个数值持续在较高的情况,很有可能是您的网路驱动部份有问题。'+ - '可以在 '+ - '内核文档中找到各个指标。'); + + netdataDashboard.sparkline('
另一个重要的指标是 softirq ', 'system.cpu', 'softirq', '%', ',若这个数值持续在较高的情况,很有可能是您的网路驱动部份有问题。'); }, valueRange: "[0, 100]" }, @@ -1128,28 +1086,28 @@ netdataDashboard.context = { }, 'system.cpu_pressure': { - info: '压力信息 ' + - '识别和量化资源争用造成的中断。 ' + - '“一些”行表示CPU上至少一些任务停滞的时间份额。 ' + - '这些比率(以%为单位)被跟踪为10秒、60秒和300秒windows的近期趋势。' + info: 'Pressure Stall Information ' + + 'identifies and quantifies the disruptions caused by resource contentions. ' + + 'The "some" line indicates the share of time in which at least some tasks are stalled on CPU. ' + + 'The ratios (in %) are tracked as recent trends over 10-, 60-, and 300-second windows.' }, 'system.memory_some_pressure': { - info: '压力信息 ' + - '识别和量化资源争用造成的中断。 ' + - '“一些”行表示至少一些任务在内存上停滞的时间份额。 ' + - '“全”行表示所有非空闲任务同时在内存上停滞的时间份额。 ' + - '在这种状态下,实际的CPU周期将被浪费,在这个状态下花费很长时间的工作负载被认为是鞭打。 ' + - '这些比率(以%为单位)被跟踪为10秒、60秒和300秒windows的近期趋势。' + info: 'Pressure Stall Information ' + + 'identifies and quantifies the disruptions caused by resource contentions. ' + + 'The "some" line indicates the share of time in which at least some tasks are stalled on memory. ' + + 'The "full" line indicates the share of time in which all non-idle tasks are stalled on memory simultaneously. ' + + 'In this state actual CPU cycles are going to waste, and a workload that spends extended time in this state is considered to be thrashing. ' + + 'The ratios (in %) are tracked as recent trends over 10-, 60-, and 300-second windows.' }, 'system.io_some_pressure': { - info: '压力信息 ' + - '识别和量化资源争用造成的中断。' + - '“一些”行表示至少一些任务在I/O上停滞的时间份额。 ' + - '“全”行表示所有非空闲任务同时在I/O上停滞的时间份额。 ' + - '在这种状态下,实际的CPU周期将被浪费,在这个状态下花费很长时间的工作负载被认为是鞭打。 ' + - '这些比率(以%为单位)被跟踪为10秒、60秒和300秒windows的近期趋势。' + info: 'Pressure Stall Information ' + + 'identifies and quantifies the disruptions caused by resource contentions. ' + + 'The "some" line indicates the share of time in which at least some tasks are stalled on I/O. ' + + 'The "full" line indicates the share of time in which all non-idle tasks are stalled on I/O simultaneously. ' + + 'In this state actual CPU cycles are going to waste, and a workload that spends extended time in this state is considered to be thrashing. ' + + 'The ratios (in %) are tracked as recent trends over 10-, 60-, and 300-second windows.' }, 'system.io': { @@ -1168,34 +1126,32 @@ netdataDashboard.context = { }, 'system.swapio': { - info: '

所有的 Swap I/O.

'+ - '输入-系统从磁盘交换到RAM的页面。 '+ - '输出 - 系统已从 RAM 交换到磁盘的页面。' + info: '所有的 Swap I/O. (netdata 会合并显示 输入输出。如果图表中没有任何数值,则表示为 0。 - 您可以修改这一页的设定,让图表显示固定的维度。' }, 'system.pgfaults': { - info: '所有的页面错误。主要页面错误表示系统正在使用其交换。您可以在应用程序监控部分找到哪些应用程序使用交换。' + info: '所有的 Page 错误. Major page faults indicates that the system is using its swap. You can find which applications use the swap at the Applications Monitoring section.' }, 'system.entropy': { colors: '#CC22AA', - info: 'Entropy,主要是用在密码学的乱数集区 (/dev/random) 如果Entropy的集区为空,需要乱数的程序可能会导致执行变慢 (这取决于每个程序使用的介面),等待集区补充。在理想情况下,有高度熵需求的系统应该要具备专用的硬体装置 (例如 TPM 装置)。您也可以安装纯软体的方案,例如 haveged,通常这些方案只会使用在伺服器上。' + info: '熵 (Entropy),主要是用在密码学的乱数集区 (/dev/random)。如果熵的集区为空,需要乱数的程序可能会导致执行变慢 (这取决于每个程序使用的介面),等待集区补充。在理想情况下,有高度熵需求的系统应该要具备专用的硬体装置 (例如 TPM 装置)。您也可以安装纯软体的方案,例如 haveged,通常这些方案只会使用在伺服器上。' }, 'system.clock_sync_state': { - info:'

系统时钟同步状态。 '+ - '强烈建议时钟与可靠的NTP服务器同步。否则, '+ - '这会导致不可预测的问题。 '+ - 'NTP守护进程可能需要几分钟(通常最多17分钟)才能选择要同步的服务器。 '+ - '

状态图:0-不同步,1-同步。

' + info:'

系统时钟同步状态. '+ + 'It is strongly recommended having the clock in sync with reliable NTP servers. Otherwise, '+ + 'it leads to unpredictable problems. '+ + 'It can take several minutes (usually up to 17) before NTP daemon selects a server to synchronize with. '+ + '

State map: 0 - not synchronized, 1 - synchronized.

' }, 'system.clock_sync_offset': { - info: '典型的NTP客户端定期轮询一个或多个NTP服务器。 '+ - '客户必须计算其 '+ - '时间偏移 '+ - '和往返延迟。 '+ - '时间偏移是两个时钟之间绝对时间的差异。' + info: '典型的NTP客户端定期轮询一个或多个NTP服务器. '+ + 'The client must compute its '+ + 'time offset '+ + 'and round-trip delay. '+ + 'Time offset is the difference in absolute time between the two clocks.' }, 'system.forks': { @@ -1205,45 +1161,33 @@ netdataDashboard.context = { 'system.intr': { colors: '#DD5555', - info: 'CPU 中断的总数。透过检查 system.interrupts,得知每一个中断的细节资讯。在 CPUs 区段提供每一个 CPU 核心的中断情形。per CPU core.' + info: 'CPU 中断的总数。透过检查 system.interrupts,得知每一个中断的细节资讯。在 CPU 区段提供每一个 CPU 核心的中断情形。' }, 'system.interrupts': { - info: 'CPU 中断的细节。在 CPUs 区段中,依据每个 CPU 核心分析中断。 per CPU core. '+ - '/proc/interrupts的最后一列提供了中断描述或注册该中断处理程序的设备名称。' + info: 'CPU 中断的细节。在 CPU 区段中,依据每个 CPU 核心分析中断。' }, 'system.hardirq_latency': { - info: '维修硬件中断的总时间。基于BCC工具中的eBPF hardirqs。' + info: '维护硬件中断所花费的总时间。基于eBPF hardirqs from BCC tools.' }, 'system.softirqs': { - info: '

系统中的软件中断总数。 '+ - '在CPU部分,对每个CPU内核进行了分析。

'+ - '

HI - 高优先级任务组。 '+ - 'TIMER - 与计时器中断相关的任务组。 '+ - 'NET_TXNET_RX-用于网络传输和接收处理。 '+ - 'BLOCK - 处理阻止I/O完成事件。 '+ - 'IRQ_POLL - IO子系统用于提高性能(块设备的一种类似NAPI的方法)。 '+ - 'TASKLET - 处理常规任务。 '+ - 'SCHED - 调度程序用于执行负载平衡和其他调度任务。 '+ - 'HRTIMER - 用于高分辨率计时器。 '+ - 'RCU - 执行读拷贝更新 (RCU) 处理。

' - + info: 'CPU softirqs 的细节。在 CPU 区段中,依据每个 CPU 核心分析 softirqs。' }, 'system.softirq_latency': { - info: '维修软件中断的总时间。基于BCC工具中的eBPF softirqs。' + info: '维护软件中断所花费的总时间。基于eBPFsoftirqs from BCC tools.' }, 'system.processes': { - info: '

系统程序。

'+ - '

Running - 显示正在 CPU 中的程序。'+ - 'Blocked - 显示目前被挡下无法进入 CPU 执行的程序,例如:正在等待磁碟完成动作,才能继续。

' + info: '

系统流程.

'+ + '

Running - running or ready to run (runnable). '+ + 'Blocked - currently blocked, waiting for I/O to complete.

' }, 'system.active_processes': { - info: '所有的系统程序。' + info: '系统中的进程总数.' }, 'system.ctxt': { @@ -1286,64 +1230,64 @@ netdataDashboard.context = { }, 'system.swapcalls': { - info: '监控对函数swap_readpageswap_writepage的调用。当启用时,Netdata还显示应用程序的交换访问权限。' + info: '监视对函数的调用 swap_readpage and swap_writepage. When integration with apps is enabled, Netdata also shows swap access per application.' }, 'system.ipc_semaphores': { - info: '分配的系统V IPC信号量。 '+ - '/proc/sys/kernel/sem文件(第二个字段)规定了所有信号量集中信号量的系统范围限制。' + info: '分配的System V IPC信号量的数量. '+ + 'The system-wide limit on the number of semaphores in all semaphore sets is specified in /proc/sys/kernel/sem file (2nd field).' }, 'system.ipc_semaphore_arrays': { - info: '使用过的System V IPC信号量阵列(集)的数量。信号量支持信号量集,其中每个信号量都是计数信号量。 '+ - '因此,当应用程序请求信号量时,内核会以集合的方式释放它们。 '+ - '/proc/sys/kernel/sem文件(第4个字段)中指定了信号量集最大数量的系统范围限制。' + info: 'Number of used System V IPC semaphore arrays (sets). Semaphores support semaphore sets where each one is a counting semaphore. '+ + 'So when an application requests semaphores, the kernel releases them in sets. '+ + 'The system-wide limit on the maximum number of semaphore sets is specified in /proc/sys/kernel/sem file (4th field).' }, 'system.shared_memory_segments': { - info: '分配的System V IPC内存段数。 '+ - '/proc/sys/kernel/shmmni文件中指定了可以创建的系统范围内共享内存段的最大数量。' + info: '分配的System V IPC内存段数. '+ + 'The system-wide maximum number of shared memory segments that can be created is specified in /proc/sys/kernel/shmmni file.' }, 'system.shared_memory_bytes': { - info: 'System V IPC内存段目前使用的内存量。 '+ - '可以创建的最大共享内存段大小的运行时限制在/proc/sys/kernel/shmmax文件中指定。' + info: 'System V IPC内存段当前使用的内存量. '+ + 'The run-time limit on the maximum shared memory segment size that can be created is specified in /proc/sys/kernel/shmmax file.' }, 'system.shared_memory_calls': { - info: '监控对函数shmgetshmatshmdtshmctl的调用。当与应用程序的集成启用时,Netdata还显示每个应用程序的共享内存系统调用使用情况。' + info: '监视对函数的调用 shmget, shmat, shmdt, and shmctl. When integration with apps is enabled, Netdata also shows shared memory system call usage per application.' }, 'system.message_queue_messages': { - info: '系统V IPC消息队列中当前存在的消息数量。' + info: '当前存在于System V IPC消息队列中的消息数。' }, 'system.message_queue_bytes': { - info: '系统V IPC消息队列中消息当前使用的内存量。' + info: 'System V IPC消息队列中消息当前使用的内存量。' }, 'system.uptime': { - info: '系统已运行的时间量,包括暂停的时间。' + info: '系统已运行的时间量,包括挂起所用的时间.' }, 'system.process_thread': { - title : 'Task creation', - info: 'do_fork,或者kernel_clone(如果您运行的内核更新于5.16)来创建新任务的次数,这是用于定义内核内进程和任务的常用名称。Netdata标识监控跟踪点sched_process_fork的线程。此图表由eBPF插件提供。' + title : '任务创建', + info: '其中一项的次数 do_fork, or kernel_clone if you are running kernel newer than 5.9.16, is called to create a new task, which is the common name used to define process and tasks inside the kernel. Netdata identifies the threads monitoring tracepoint sched_process_fork. This chart is provided by eBPF plugin.' }, 'system.exit': { - title : 'Exit monitoring', - info: '呼吁负责关闭的功能(do_exit)和发布(release_task)任务。此图表由eBPF插件提供。' + title : '退出监控', + info: '调用负责关闭的函数 (do_exit) and releasing (release_task) tasks. This chart is provided by eBPF plugin.' }, 'system.task_error': { - title : 'Task error', - info: '创建新进程或线程的错误数量。此图表由eBPF插件提供。' + title : '任务错误', + info: '创建新进程或线程的错误数。此图表由eBPF插件提供.' }, 'system.process_status': { - title : 'Task status', - info: '创建的进程数量和每个周期创建的线程数量(process维度)之间的差异,它还显示了在系统上运行的可能僵尸进程的数量。此图表由eBPF插件提供。' + title : '任务状态', + info: '每个期间创建的进程数和创建的线程数之间的差异(process dimension), it also shows the number of possible zombie process running on system. This chart is provided by eBPF plugin.' }, // ------------------------------------------------------------------------ @@ -1371,41 +1315,41 @@ netdataDashboard.context = { }, 'cpu.core_throttling': { - info: '根据CPU的核心温度对CPU的时钟速度所做的调整次数。' + info: '根据CPU核心温度对CPU时钟速度进行的调整次数.' }, 'cpu.package_throttling': { - info: '根据CPU的封装(芯片)温度对CPU的时钟速度进行的调整次数。' + info: '根据CPU的封装(芯片)温度对CPU时钟速度进行的调整次数.' }, 'cpufreq.cpufreq': { - info: '频率测量CPU每秒执行的周期数。' + info: '频率测量CPU每秒执行的周期数.' }, 'cpuidle.cpuidle': { - info: '在C-states中花费的时间百分比' + info: '在C状态花费的时间百分比.' }, // ------------------------------------------------------------------------ // MEMORY 'mem.ksm': { - info: '

内存页面合并统计数据。 '+ - '共享共享的高比率表示良好的共享, '+ - '但未共享共享的高比率表明浪费了精力。

'+ - '

共享 - 使用共享页面。 '+ - '未共享 - 内存不再共享(页面是唯一的,但反复检查合并)。 '+ - '共享-当前共享的内存(有多少个网站正在共享页面,即保存了多少)。 '+ - '易变 - 易变页面(变化太快,无法放在树上)。

' + info: '

内存页合并统计信息. '+ + 'A high ratio of Sharing to Shared indicates good sharing, '+ + 'but a high ratio of Unshared to Sharing indicates wasted effort.

'+ + '

Shared - used shared pages. '+ + 'Unshared - memory no longer shared (pages are unique but repeatedly checked for merging). '+ + 'Sharing - memory currently shared (how many more sites are sharing the pages, i.e. how much saved). '+ + 'Volatile - volatile pages (changing too fast to be placed in a tree).

' }, 'mem.ksm_savings': { heads: [ netdataDashboard.gaugeChart('Saved', '12%', 'savings', '#0099CC') ], - info: '

KSM节省的内存量。

'+ - '

节省 - 保存内存。 '+ - '提供 - 标记为可合并的内存。

' + info: '

KSM保存的内存量.

'+ + '

Savings - 保存的内存. '+ + 'Offered - 标记为可合并的内存.

' }, 'mem.ksm_ratios': { @@ -1425,23 +1369,23 @@ netdataDashboard.context = { + ' role="application">'; } ], - info: 'The effectiveness of KSM. '+ - '这是当前合并的可合并页面的百分比。' + info: 'KSM的有效性. '+ + '这是当前合并的可合并页面的百分比.' }, 'mem.zram_usage': { - info: 'ZRAM总RAM使用指标。ZRAM使用一些内存来存储有关存储内存页面的元数据,从而引入了与磁盘大小成正比的开销。它排除了相同元素填充的页面,因为没有为它们分配内存。' + info: 'ZRAM total RAM usage metrics. ZRAM uses some memory to store metadata about stored memory pages, thus introducing an overhead which is proportional to disk size. It excludes same-element-filled-pages since no memory is allocated for them.' }, 'mem.zram_savings': { - info: '显示原始和压缩内存数据大小。' + info: '显示原始和压缩内存数据大小.' }, 'mem.zram_ratio': { heads: [ netdataDashboard.gaugeChart('Compression Ratio', '12%', 'ratio', '#0099CC') ], - info: '压缩率,计算为100 * original_size / compressed_size。更多意味着更好的压缩和更多的RAM节省。' + info: 'Compression ratio, calculated as 100 * original_size / compressed_size. More means better compression and more RAM savings.' }, 'mem.zram_efficiency': { @@ -1451,17 +1395,17 @@ netdataDashboard.context = { commonMin: true, commonMax: true, valueRange: "[0, 100]", - info: '内存使用效率,计算为100 * compressed_size / total_mem_used。' + info: 'Memory usage efficiency, calculated as 100 * compressed_size / total_mem_used.' }, 'mem.pgfaults': { - info: '

页面错误是一种中断, '+ - '称为陷阱,当运行中的程序访问内存页面时,由计算机硬件引发 '+ - '映射到虚拟地址空间,但实际上没有加载到主内存中。

'+ - '

次要-页面在生成故障时加载到内存中, '+ - '但在内存管理单元中未标记为正在加载内存中。 '+ - '主要-当系统需要从磁盘加载内存页面或交换内存时生成。

' + info: '

A page fault 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.

'+ + '

Minor - 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. '+ + 'Major - generated when the system needs to load the memory page from disk or swap memory.

' }, 'mem.committed': { @@ -1470,30 +1414,30 @@ netdataDashboard.context = { }, 'mem.oom_kill': { - info: '被杀死的进程数量 '+ - '内存不足杀手。 '+ - '当系统缺少可用内存时,内核的OOM杀手会被召唤,并且 '+ - '无法在不杀死一个或多个进程的情况下进行。 '+ - '它试图选择其消亡将释放最多记忆的过程,同时 '+ - '给系统用户带来最少的痛苦。 '+ - '此计数器还包括容器中超过内存限制的进程。' + info: '被终止的进程数 '+ + 'Out of Memory Killer. '+ + 'The kernel\'s OOM killer is summoned when the system runs short of free memory and '+ + 'is unable to proceed without killing one or more processes. '+ + 'It tries to pick the process whose demise will free the most memory while '+ + 'causing the least misery for users of the system. '+ + 'This counter also includes processes within containers that have exceeded the memory limit.' }, 'mem.numa': { - info: '

NUMA平衡统计数据。

'+ - '

本地-通过此节点上的进程成功分配了页面。 '+ - '外国 - 最初用于分配给另一个节点的页面。 '+ - '交错-交错策略页面已成功分配给此节点。 '+ - '其他-通过另一个节点上的进程在这个节点上分配的页面。 '+ - 'PteUpdates - 标记为NUMA提示故障的基页。 '+ - 'HugePteUpdates - 标记为NUMA提示故障的透明大页面。 '+ - '与pte_updates相结合,可以计算标记的总地址空间。 '+ - 'HintFaults - NUMA暗示被困的故障。 '+ - 'HintFaultsLocal - 提示本地节点的故障。 '+ - '结合提示故障,可以计算局部故障与远程故障的百分比。 '+ - '很高比例的局部提示故障表明工作量更接近收敛。 '+ - 'PagesMigrated - 页面被迁移,因为它们放错了地方。 '+ - '由于迁移是一种复制操作,它贡献了NUMA平衡产生的开销的最大部分。

' + info: '

NUMA平衡统计.

'+ + '

Local - pages successfully allocated on this node, by a process on this node. '+ + 'Foreign - pages initially intended for this node that were allocated to another node instead. '+ + 'Interleave - interleave policy pages successfully allocated to this node. '+ + 'Other - pages allocated on this node, by a process on another node. '+ + 'PteUpdates - base pages that were marked for NUMA hinting faults. '+ + 'HugePteUpdates - transparent huge pages that were marked for NUMA hinting faults. '+ + 'In Combination with pte_updates the total address space that was marked can be calculated. '+ + 'HintFaults - NUMA hinting faults that were trapped. '+ + 'HintFaultsLocal - hinting faults that were to local nodes. '+ + 'In combination with HintFaults, the percentage of local versus remote faults can be calculated. '+ + 'A high percentage of local hinting faults indicates that the workload is closer to being converged. '+ + 'PagesMigrated - pages were migrated because they were misplaced. '+ + 'As migration is a copying operation, it contributes the largest part of the overhead created by NUMA balancing.

' }, 'mem.available': { @@ -1505,260 +1449,260 @@ netdataDashboard.context = { }, 'mem.kernel': { - info: '

内核使用的总内存量。

'+ - '

Slab - 内核用于缓存数据结构供自己使用。 '+ - 'KernelStack - 为内核完成的每个任务分配。 '+ - 'PageTables - 专用于最低级别的页面表(页面表用于将虚拟地址转换为物理内存地址)。 '+ - 'VmallocUsed-用作虚拟地址空间。 '+ - 'Percpu - 分配给用于支持每个CPU分配的每个CPU分配器(不包括元数据成本)。 '+ - '当您创建每个CPU变量时,系统上的每个处理器都会获得该变量的副本。

' + info: '

内核使用的内存总量.

'+ + '

Slab - used by the kernel to cache data structures for its own use. '+ + 'KernelStack - allocated for each task done by the kernel. '+ + 'PageTables - dedicated to the lowest level of page tables (A page table is used to turn a virtual address into a physical memory address). '+ + 'VmallocUsed - being used as virtual address space. '+ + 'Percpu - allocated to the per-CPU allocator used to back per-CPU allocations (excludes the cost of metadata). '+ + 'When you create a per-CPU variable, each processor on the system gets its own copy of that variable.

' }, 'mem.slab': { - info: '

平板内存统计。

'+ - '

可回收 - 内核可以重用的内存量。 '+ - '不可回收 - 即使内核缺乏内存,也无法重用。

' + info: '

Slab memory statistics.

'+ + '

Reclaimable - amount of memory which the kernel can reuse. '+ + 'Unreclaimable - can not be reused even when the kernel is lacking memory.

' }, 'mem.hugepages': { - info: '专用(或直接)大型页面是为配置为使用大型页面的应用程序保留的内存。巨页使用内存,即使有免费的巨页可用。' + info: 'Dedicated (or Direct) HugePages is memory reserved for applications configured to utilize huge pages. Hugepages are used memory, even if there are free hugepages available.' }, 'mem.transparent_hugepages': { - info: '透明巨页(THP)用巨页支持虚拟内存,支持页面大小的自动推广和降级。它适用于匿名内存映射和tmpfs/shmem的所有应用程序。' + info: 'Transparent HugePages (THP) is backing virtual memory with huge pages, supporting automatic promotion and demotion of page sizes. It works for all applications for anonymous memory mappings and tmpfs/shmem.' }, 'mem.hwcorrupt': { - info: '存在物理损坏问题的内存量,由ECC识别,并由内核预留,使其不被使用。' + info: '存在物理损坏问题的内存量,由 ECC and set aside by the kernel so it does not get used.' }, 'mem.ecc_ce': { - info: '可更正(单位)ECC错误的数量。 '+ - '这些错误不影响系统的正常运行 '+ - '因为他们仍在纠正。 '+ - '周期性可更正错误可能表明其中一个内存模块正在缓慢故障。' + info: '存在物理损坏问题的内存量,由. '+ + 'These errors do not affect the normal operation of the system '+ + 'because they are still being corrected. '+ + 'Periodic correctable errors may indicate that one of the memory modules is slowly failing.' }, 'mem.ecc_ue': { - info: '无法更正(多位)ECC错误的数量。 '+ - '无法更正的错误是一个致命的问题,通常会导致操作系统崩溃。' + info: '无法纠正的(多位)ECC错误数. '+ + 'An uncorrectable error is a fatal issue that will typically lead to an OS crash.' }, 'mem.pagetype_global': { - info: '以一定大小的块为单位的可用内存量。' + info: '在一定大小的块中可用的内存量.' }, 'mem.cachestat_ratio': { - info: '当处理器需要读取或写入主内存中的位置时,它会检查页面缓存中的相应条目。如果条目在那里,则发生了页面缓存命中,并且读取来自缓存。如果没有条目,则会发生页面缓存丢失,内核会分配一个新的条目并从磁盘中复制数据。Netdata计算内存上缓存的访问文件的百分比。计算的比率是计算访问的缓存页面(不计算脏页面和因读取丢失而添加的页面)除以没有脏页面的总访问量。' + info: 'When the processor needs to read or write a location in main memory, it checks for a corresponding entry in the page cache. If the entry is there, a page cache hit has occurred and the read is from the cache. If the entry is not there, a page cache miss has occurred and the kernel allocates a new entry and copies in data from the disk. Netdata calculates the percentage of accessed files that are cached on memory. The ratio is calculated counting the accessed cached pages (without counting dirty pages and pages added because of read misses) divided by total access without dirty pages.' }, 'mem.cachestat_dirties': { - info: '肮脏(修改)页面缓存的数量。引入后修改的页面缓存中的页面称为脏页面。由于页面缓存中的非脏页面在辅助存储(例如硬盘驱动器或固态驱动器)中具有相同的副本,因此丢弃和重用其空间比分页应用程序内存快得多,通常比将脏页面冲入辅助存储并重复使用其空间更可取。' + info: 'Number of dirty(modified) pages cache. Pages in the page cache modified after being brought in are called dirty pages. Since non-dirty pages in the page cache have identical copies in secondary storage (e.g. hard disk drive or solid-state drive), discarding and reusing their space is much quicker than paging out application memory, and is often preferred over flushing the dirty pages into secondary storage and reusing their space.' }, 'mem.cachestat_hits': { - info: '当处理器需要读取或写入主内存中的位置时,它会检查页面缓存中的相应条目。如果条目在那里,则发生了页面缓存命中,并且读取来自缓存。点击量显示未修改的访问页面(我们排除脏页面),此计数还不包括最近插入供阅读的页面。' + info: 'When the processor needs to read or write a location in main memory, it checks for a corresponding entry in the page cache. If the entry is there, a page cache hit has occurred and the read is from the cache. Hits show pages accessed that were not modified (we are excluding dirty pages), this counting also excludes the recent pages inserted for read.' }, 'mem.cachestat_misses': { - info: '当处理器需要读取或写入主内存中的位置时,它会检查页面缓存中的相应条目。如果没有条目,则发生页面缓存丢失,缓存分配新条目并复制主内存的数据。缺少与编写无关的内存的页面插入计数。' + info: 'When the processor needs to read or write a location in main memory, it checks for a corresponding entry in the page cache. If the entry is not there, a page cache miss has occurred and the cache allocates a new entry and copies in data for the main memory. Misses count page insertions to the memory not related to writing.' }, 'mem.sync': { - info: '系统调用sync()和syncfs(),这将文件系统缓冲区刷新到存储设备。这些通话可能会造成性能扰动。sync()调用基于BCC工具中的eBPF syncsnoop。' + info: 'System calls for sync() and syncfs() which flush the file system buffers to storage devices. Performance perturbations might be caused by these calls. The sync() calls are based on the eBPF syncsnoop from BCC tools.' }, 'mem.file_sync': { - info: '系统调用fsync()和fdatasync()传输磁盘设备上文件的所有修改页面缓存。这些通话会阻止,直到设备报告转接已完成。' + info: 'System calls for fsync() and fdatasync() transfer all modified page caches for the files on disk devices. These calls block until the device reports that the transfer has been completed.' }, 'mem.memory_map': { - info: '系统调用msync(),该更改刷新了对映射文件的核心副本所做的更改。' + info: 'System calls for msync() which flushes changes made to the in-core copy of a file that was mapped.' }, 'mem.file_segment': { - info: 'sync_file_range()的系统调用允许在将文件描述符fd引用的打开文件与磁盘同步时进行精细控制。这种系统调用极其危险,不应用于便携式程序。' + info: 'System calls for sync_file_range() permits fine control when synchronizing the open file referred to by the file descriptor fd with disk. This system call is extremely dangerous and should not be used in portable programs.' }, 'filesystem.dc_hit_ratio': { - info: '目录缓存中存在的文件访问百分比。100%表示访问的每个文件都存在于目录缓存中。如果目录缓存中不存在文件1)它们不存在于文件系统中,2)以前没有访问过文件。阅读更多关于目录缓存的信息。当与应用程序的集成启用时,Netdata还根据应用程序显示目录缓存。' + info: 'Percentage of file accesses that were present in the directory cache. 100% means that every file that was accessed was present in the directory cache. If files are not present in the directory cache 1) they are not present in the file system, 2) the files were not accessed before. Read more about directory cache. When integration with apps is enabled, Netdata also shows directory cache per application.' }, 'filesystem.dc_reference': { - info: '文件访问计数器。引用是当有文件访问且文件不存在于目录缓存中时。Miss是当有文件访问且文件系统中找不到文件时。是指有文件访问,文件存在于文件系统中,但不存在于目录缓存中。阅读更多关于目录缓存的信息。' + info: 'Counters of file accesses. Reference is when there is a file access and the file is not present in the directory cache. Miss is when there is file access and the file is not found in the filesystem. Slow is when there is a file access and the file is present in the filesystem but not in the directory cache. Read more about directory cache.' }, 'md.health': { - info: '每个MD阵列的故障设备数量。 '+ - 'Netdata从md状态行的[n/m]字段检索此数据。 '+ - '这意味着理想情况下,数组将有n设备,但目前,m设备正在使用中。 '+ - '失败磁盘n-m。' + info: 'Number of failed devices per MD array. '+ + 'Netdata retrieves this data from the [n/m] field of the md status line. '+ + 'It means that ideally the array would have n devices however, currently, m devices are in use. '+ + 'failed disks is n-m.' }, 'md.disks': { - info: '处于使用和处于停机状态的设备数量。 '+ - 'Netdata从md状态行的[n/m]字段检索此数据。 '+ - '这意味着理想情况下,数组将有n设备,但目前,m设备正在使用中。 '+ - 'inusemdownn-m。' + info: 'Number of devices in use and in the down state. '+ + 'Netdata retrieves this data from the [n/m] field of the md status line. '+ + 'It means that ideally the array would have n devices however, currently, m devices are in use. '+ + 'inuse is m, down is n-m.' }, 'md.status': { - info: '完成正在进行的业务的进展。' + info: 'Completion progress of the ongoing operation.' }, 'md.expected_time_until_operation_finish': { - info: '完成正在进行的操作的预计时间。 '+ - '时间只是一个近似值,因为操作速度将根据其他I/O要求而变化。' + info: 'Estimated time to complete the ongoing operation. '+ + 'The time is only an approximation since the operation speed will vary according to other I/O demands.' }, 'md.operation_speed': { - info: '持续运营的速度。 '+ - '/proc/sys/dev/raid/{speed_limit_min,speed_limit_max}文件中指定了系统范围的重建速度限制。 '+ - '这些选项有利于调整重建过程,并可能增加整体系统负载、cpu和内存使用率。' + info: 'Speed of the ongoing operation. '+ + 'The system-wide rebuild speed limits are specified in /proc/sys/dev/raid/{speed_limit_min,speed_limit_max} files. '+ + 'These options are good for tweaking rebuilt process and may increase overall system load, cpu and memory usage.' }, 'md.mismatch_cnt': { - info: '在执行检查修复时,以及可能在执行重新同步时,md将计算发现的错误数量。 '+ - '不匹配计数记录在sysfs文件md/mismatch_cnt中。 '+ - '此值是重写或(对于check)将重写的扇区数量。 '+ - '它可能大于页面扇区数量的因子的实际错误数量。 '+ - '在RAID1或RAID10上无法非常可靠地解释不匹配,特别是在设备用于交换时。 '+ - '在真正干净的RAID5或RAID6阵列上,任何不匹配都应表明在某些级别上存在硬件问题- '+ - '软件问题绝不应导致此类不匹配。 '+ - '有关详细信息,请参阅md(4)。' + info: 'When performing check and repair, and possibly when performing resync, md will count the number of errors that are found. '+ + 'A count of mismatches is recorded in the sysfs file md/mismatch_cnt. '+ + 'This value is the number of sectors that were re-written, or (for check) would have been re-written. '+ + 'It may be larger than the number of actual errors by a factor of the number of sectors in a page. '+ + 'Mismatches can not be interpreted very reliably on RAID1 or RAID10, especially when the device is used for swap. '+ + 'On a truly clean RAID5 or RAID6 array, any mismatches should indicate a hardware problem at some level - '+ + 'software issues should never cause such a mismatch. '+ + 'For details, see md(4).' }, 'md.flush': { - info: '每个MD阵列的刷新计数。基于BCC工具中的eBPF mdflush。' + info: 'Number of flush counts per MD array. Based on the eBPF mdflush from BCC tools.' }, // ------------------------------------------------------------------------ // IP 'ip.inerrors': { - info: '

接收IP数据包时遇到的错误数量。

' + - '

NoRoutes - 由于没有发送路线而删除的数据包。 ' + - 'Truncated - 由于数据报帧没有携带足够的数据而被丢弃的数据包。 ' + - '校验和-因校验和错误而删除的数据包。

' + info: '

在接收IP数据包期间遇到的错误数.

' + + '

NoRoutes - packets that were dropped because there was no route to send them. ' + + 'Truncated - packets which is being discarded because the datagram frame didn\'t carry enough data. ' + + 'Checksum - packets that were dropped because they had wrong checksum.

' }, 'ip.mcast': { - info: '系统中的总多播流量。' + info: '系统中的总多播流量.' }, 'ip.mcastpkts': { - info: '系统中传输的多播数据包总数。' + info: '系统中传输的多播数据包总数.' }, 'ip.bcast': { - info: '系统中的总广播流量。' + info: '系统中的总广播流量.' }, 'ip.bcastpkts': { - info: '系统中传输的广播数据包总数。' + info: '系统中传输的广播数据包总数.' }, 'ip.ecnpkts': { - info: '

系统中设置了ECN位的接收IP数据包总数。

'+ - '

CEP - 遇到拥堵。 '+ - 'NoECTP - 不支持ECN的运输。 '+ - 'ECTP0ECTP1-支持ECN的传输。

' + info: '

系统中设置ECN位的接收IP数据包总数.

'+ + '

CEP - congestion encountered. '+ + 'NoECTP - non ECN-capable transport. '+ + 'ECTP0 and ECTP1 - ECN capable transport.

' }, 'ip.tcpreorders': { - info: '

TCP通过按正确的顺序排序数据包或防止数据包失序 '+ - '通过请求重新传输出序的数据包。

'+ - '

时间戳 - 使用时间戳选项检测到重新排序。 '+ - 'SACK - 使用选择性确认算法检测到重新排序。 '+ - 'FACK - 使用正向确认算法检测到重新排序。 '+ - 'Reno - 使用快速重新传输算法检测到重新排序。

' + info: '

TCP通过按正确的顺序对数据包进行排序或 '+ + 'by requesting the retransmission of out-of-order packets.

'+ + '

Timestamp - detected re-ordering using the timestamp option. '+ + 'SACK - detected re-ordering using Selective Acknowledgment algorithm. '+ + 'FACK - detected re-ordering using Forward Acknowledgment algorithm. '+ + 'Reno - detected re-ordering using Fast Retransmit algorithm.

' }, 'ip.tcpofo': { - info: '

TCP维护一个无序队列,以在TCP通信中保留无序数据包。

'+ - '

InQueue - TCP层收到一个无序的数据包,并有足够的内存排队。 '+ - 'Droppped - TCP层收到一个无序的数据包,但没有足够的内存,因此将其删除。 '+ - '合并 - 收到的无序数据包与上一个数据包具有覆盖。 '+ - '覆盖部分将被删除。所有这些数据包也将计入InQueue。 '+ - '修剪 - 由于套接字缓冲区溢出,数据包从无序队列中删除。

' + info: '

TCP维护一个无序队列,以保持TCP通信中无序的数据包.

'+ + '

InQueue - the TCP layer receives an out-of-order packet and has enough memory to queue it. '+ + 'Dropped - the TCP layer receives an out-of-order packet but does not have enough memory, so drops it. '+ + 'Merged - the received out-of-order packet has an overlay with the previous packet. '+ + 'The overlay part will be dropped. All these packets will also be counted into InQueue. '+ + 'Pruned - packets dropped from out-of-order queue because of socket buffer overrun.

' }, 'ip.tcpsyncookies': { info: '

SYN cookies '+ - '用于缓解SYN洪水。

'+ - '

收到-发送SYN Cookie后,它回到我们身边并通过了支票。'+ - '发送 - 应用程序无法足够快地接受连接,因此内核无法存储 '+ - '此连接队列中的条目。它没有删除它,而是向客户端发送了一个SYN cookie。 '+ - '失败-从SYN Cookie解码的MSS无效。当这个计数器递增时, '+ - '接收的数据包不会被视为SYN Cookie。

' + 'are used to mitigate SYN flood.

'+ + '

Received - after sending a SYN cookie, it came back to us and passed the check. '+ + 'Sent - an application was not able to accept a connection fast enough, so the kernel could not store '+ + 'an entry in the queue for this connection. Instead of dropping it, it sent a SYN cookie to the client. '+ + 'Failed - the MSS decoded from the SYN cookie is invalid. When this counter is incremented, '+ + 'the received packet won’t be treated as a SYN cookie.

' }, 'ip.tcpmemorypressures': { - info: '套接字因非致命内存分配失败而施加内存压力的次数 '+ - '(内核试图通过减少发送缓冲区等来解决这个问题)。' + info: 'The number of times a socket was put in memory pressure due to a non fatal memory allocation failure '+ + '(the kernel attempts to work around this situation by reducing the send buffers, etc).' }, 'ip.tcpconnaborts': { - info: '

TCP连接中止。

'+ - '

BadData - 当连接在FIN_WAIT1上且内核收到数据包时发生 '+ - '此连接的序列号超过最后一个序列号- '+ - '内核使用RST响应(关闭连接)。 '+ - 'UserClosed - 当内核在已关闭的连接上接收数据并 '+ - '用RST回复。 '+ - 'NoMemory - 当Orphan插太多(未连接到fd)和 '+ - '内核必须删除连接——有时它会发送RST,有时不会。 '+ - '超时 - 当连接超时发生。 '+ - 'Linger - 当内核杀死已被应用程序关闭的套接字并 '+ - '徘徊了足够长的时间。 '+ - '失败 - 当内核尝试发送 RST 但因没有可用内存而失败时发生。

' + info: '

TCP connection aborts.

'+ + '

BadData - happens while the connection is on FIN_WAIT1 and the kernel receives a packet '+ + 'with a sequence number beyond the last one for this connection - '+ + 'the kernel responds with RST (closes the connection). '+ + 'UserClosed - happens when the kernel receives data on an already closed connection and '+ + 'responds with RST. '+ + 'NoMemory - 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 RST, sometimes it won\'t. '+ + 'Timeout - happens when a connection times out. '+ + 'Linger - happens when the kernel killed a socket that was already closed by the application and '+ + 'lingered around for long enough. '+ + 'Failed - happens when the kernel attempted to send an RST but failed because there was no memory available.

' }, 'ip.tcp_functions': { - title : 'TCP calls', - info: '对函数tcp_sendmsgtcp_cleanup_rbuftcp_close的调用成功或失败。' + title : 'TCP调用', + info: 'Successful or failed calls to functions tcp_sendmsg, tcp_cleanup_rbuf, and tcp_close.' }, 'ip.total_tcp_bandwidth': { - title : 'TCP bandwidth', - info: '由函数tcp_sendmsgtcp_cleanup_rbuf发送和接收的字节。我们使用tcp_cleanup_rbuf而不是tcp_recvmsg,因为最后一个错过了tcp_read_sock()流量,我们还需要有更多的探针来获得套接字和包大小。' + title : 'TCP带宽', + info: 'Bytes sent and received by functions tcp_sendmsg and tcp_cleanup_rbuf. We use tcp_cleanup_rbuf instead of tcp_recvmsg, because the last one misses tcp_read_sock() traffic and we would also need to have more probes to get the socket and package size.' }, 'ip.tcp_error': { - title : 'TCP errors', - info: '对函数tcp_sendmsgtcp_cleanup_rbuftcp_close的调用失败。' + title : 'TCP错误', + info: 'Failed calls to functions tcp_sendmsg, tcp_cleanup_rbuf, and tcp_close.' }, 'ip.tcp_retransmit': { - title : 'TCP retransmit', - info: '通过函数tcp_retransmit_skb重新传输的数据包数量。' + title : 'TCP重新传输', + info: 'Number of packets retransmitted by function tcp_retransmit_skb.' }, 'ip.udp_functions': { - title : 'UDP calls', - info: '对函数udp_sendmsgudp_recvmsg的调用成功或失败。' + title : 'UDP呼叫', + info: 'Successful or failed calls to functions udp_sendmsg and udp_recvmsg.' }, 'ip.total_udp_bandwidth': { - title : 'UDP bandwidth', - info: '由函数udp_sendmsgudp_recvmsg发送和接收的字节。' + title : 'UDP带宽', + info: 'Bytes sent and received by functions udp_sendmsg and udp_recvmsg.' }, 'ip.udp_error': { - title : 'UDP errors', - info: '对函数udp_sendmsgudp_recvmsg的调用失败。' + title : 'UDP错误', + info: 'Failed calls to functions udp_sendmsg and udp_recvmsg.' }, 'ip.tcp_syn_queue': { - info: '

内核的SYN队列跟踪TCP握手,直到连接完全建立。 ' + - '当太多传入的TCP连接请求处于半开放状态和服务器时,它会溢出 ' + - '未配置回退到SYN Cookie。溢出通常由SYN洪水DoS攻击引起。

' + - '

Drops - 由于SYN队列已满且SYN cookie被禁用,连接数量下降。 ' + - 'Cookies - 由于SYN队列已满而发送的SYN Cookie数量。

' + info: '

内核的SYN队列跟踪TCP握手,直到连接完全建立. ' + + 'It overflows when too many incoming TCP connection requests hang in the half-open state and the server ' + + 'is not configured to fall back to SYN cookies. Overflows are usually caused by SYN flood DoS attacks.

' + + '

Drops - number of connections dropped because the SYN queue was full and SYN cookies were disabled. ' + + 'Cookies - number of SYN cookies sent because the SYN queue was full.

' }, 'ip.tcp_accept_queue': { - info: '

内核的接受队列持有完全建立的TCP连接,等待处理 ' + - '通过收听应用程序。

'+ - '溢出 - 因 '+ - '监听应用程序的接收队列已满。 '+ - 'Drops - 无法处理的传入连接数量,包括SYN洪水, '+ - '溢出、内存不足、安全问题、没有前往目的地的路线、接收相关的ICMP消息、 '+ - '套接字是广播或多播。

' + info: '

内核的接受队列保存完全建立的TCP连接,等待处理 ' + + 'by the listening application.

'+ + 'Overflows - the number of established connections that could not be handled because '+ + 'the receive queue of the listening application was full. '+ + 'Drops - number of incoming connections that could not be handled, including SYN floods, '+ + 'overflows, out of memory, security issues, no route to destination, reception of related ICMP messages, '+ + 'socket is broadcast or multicast.

' }, @@ -1766,368 +1710,368 @@ netdataDashboard.context = { // IPv4 'ipv4.packets': { - info: '

此主机的IPv4数据包统计。

'+ - '

已收到 - IP 层接收的数据包。 '+ - '即使稍后删除数据包,这个计数器也会增加。 '+ - '发送-通过IP层发送的数据包,适用于单播和多播数据包。 '+ - '此计数器不包括转发中计算的任何数据包。 '+ - '转发 - 此主机不是其最终IP目的地的输入数据包, '+ - '结果,有人试图找到一条路线将他们转发到最终目的地。 '+ - '在不充当IP网关的主机中,此计数器将仅包括那些 '+ + info: '

此主机的IPv4数据包统计信息.

'+ + '

Received - IP层接收的数据包. '+ + 'This counter will be increased even if the packet is dropped later. '+ + 'Sent - packets sent via IP layer, for both single cast and multicast packets. '+ + 'This counter does not include any packets counted in Forwarded. '+ + 'Forwarded - input packets for which this host was not their final IP destination, '+ + 'as a result of which an attempt was made to find a route to forward them to that final destination. '+ + 'In hosts which do not act as IP Gateways, this counter will include only those packets which were '+ 'Source-Routed '+ - '源路由选项处理成功。 '+ - '已交付 - 交付到上层协议的数据包,例如TCP、UDP、ICMP等。

' + 'and the Source-Route option processing was successful. '+ + 'Delivered - packets delivered to the upper layer protocols, e.g. TCP, UDP, ICMP, and so on.

' }, 'ipv4.fragsout': { info: '

IPv4碎片 '+ - '此系统的统计数据。

'+ - '

好的 - 已成功碎片化的数据包。 '+ - '失败 - 由于需要碎片化而被丢弃的数据包 '+ - '但不能,例如,由于Don\'t Fragment (DF)标志已设置。 '+ - '创建-因碎片生成的碎片。

' + '此系统的统计信息.

'+ + '

OK - packets that have been successfully fragmented. '+ + 'Failed - packets that have been discarded because they needed to be fragmented '+ + 'but could not be, e.g. due to Don\'t Fragment (DF) flag was set. '+ + 'Created - fragments that have been generated as a result of fragmentation.

' }, 'ipv4.fragsin': { info: '

IPv4重新组装 '+ - '此系统的统计数据。

'+ - '

好的 - 已成功重新组装的数据包。 '+ - '失败 - IP 重新组装算法检测到故障。 '+ - '这不一定是被丢弃的IP片段的计数,因为一些算法 '+ - '通过在收到碎片时进行组合,可能会丢失碎片数量。 '+ - '所有-收到需要重新组装的IP片段。

' + '此系统的统计信息.

'+ + '

OK - packets that have been successfully reassembled. '+ + 'Failed - failures detected by the IP reassembly algorithm. '+ + 'This is not necessarily a count of discarded IP fragments since some algorithms '+ + 'can lose track of the number of fragments by combining them as they are received. '+ + 'All - received IP fragments which needed to be reassembled.

' }, 'ipv4.errors': { - info: '

丢弃的IPv4数据包的数量。

'+ - '

InDiscardsOutDiscards-选择的入站和出站数据包 '+ - '即使没有出错,也要被丢弃 '+ - '检测到以防止它们交付到更高级别的协议。 '+ - 'InHdrErrors - 由于IP头错误而被丢弃的输入数据包,包括 '+ - '校验和不好,版本号不匹配,其他格式错误,超过生存时间, '+ - '在处理他们的IP选项等时发现的错误。 '+ - 'OutNoRoutes - 由于找不到路线而被丢弃的数据包 '+ - '将它们传输到目的地。这包括主机无法路由的任何数据包 '+ - '因为它所有的默认网关都关闭了。 '+ - 'InAddrErrors - 由于IP地址无效或 '+ - '目标IP地址不是本地地址,并且没有启用IP转发。 '+ - 'InUnknownProtos - 由于未知或不支持的协议而被丢弃的输入数据包。

' + info: '

丢弃的IPv4数据包数.

'+ + '

InDiscards, OutDiscards - inbound and outbound packets which were chosen '+ + 'to be discarded even though no errors had been '+ + 'detected to prevent their being deliverable to a higher-layer protocol. '+ + 'InHdrErrors - input packets that have been discarded due to errors in their IP headers, including '+ + 'bad checksums, version number mismatch, other format errors, time-to-live exceeded, '+ + 'errors discovered in processing their IP options, etc. '+ + 'OutNoRoutes - packets that have been discarded because no route could be found '+ + 'to transmit them to their destination. This includes any packets which a host cannot route '+ + 'because all of its default gateways are down. '+ + 'InAddrErrors - input packets that have been discarded due to invalid IP address or '+ + 'the destination IP address is not a local address and IP forwarding is not enabled. '+ + 'InUnknownProtos - input packets which were discarded because of an unknown or unsupported protocol.

' }, 'ipv4.icmp': { - info: '

传输的IPv4 ICMP消息的数量。

'+ - '

收到发送-主机收到并试图发送的ICMP消息。 '+ - '这两个计数器都包含错误。

' + info: '

传输的IPv4 ICMP消息数.

'+ + '

Received, Sent - ICMP messages which the host received and attempted to send. '+ + 'Both these counters include errors.

' }, 'ipv4.icmp_errors': { - info: '

IPv4 ICMP错误的数量。

'+ - '

InErrors - 收到ICMP消息,但确定存在ICMP特定错误, '+ - '例如,ICMP校验和不好,长度不好等。 '+ - 'OutErrors - 此主机由于 '+ - '在ICMP中发现的问题,如缺乏缓冲区。 '+ - '此计数器不包括在ICMP层外发现的错误 '+ - '例如IP无法路由生成的数据报。 '+ - 'InCsumErrors-收到校验和不良的ICMP消息。

' + info: '

IPv4 ICMP错误数.

'+ + '

InErrors - received ICMP messages but determined as having ICMP-specific errors, '+ + 'e.g. bad ICMP checksums, bad length, etc. '+ + 'OutErrors - ICMP messages which this host did not send due to '+ + 'problems discovered within ICMP such as a lack of buffers. '+ + 'This counter does not include errors discovered outside the ICMP layer '+ + 'such as the inability of IP to route the resultant datagram. '+ + 'InCsumErrors - received ICMP messages with bad checksum.

' }, 'ipv4.icmpmsg': { - info: '转让数量 '+ - 'IPv4 ICMP控制消息。' + info: '已传输的数量 '+ + 'IPv4 ICMP control messages.' }, 'ipv4.udppackets': { - info: '传输的UDP数据包的数量。' + info: '传输的UDP数据包数.' }, 'ipv4.udperrors': { - info: '

在传输UDP数据包时遇到的错误数量。

'+ - 'RcvbufErrors - 接收缓冲区已满。 '+ - 'SndbufErrors - 发送缓冲区已满,没有可用的内核内存,或 '+ - 'IP层在尝试发送数据包时报告了错误,并且没有设置错误队列。 '+ - 'InErrors - 这是所有错误的聚合计数器,不包括NoPorts。 '+ - 'NoPorts - 没有应用程序在目标端口监听。 '+ - 'InCsumErrors - 检测到UDP校验和失败。 '+ - '忽略多 - 忽略多播数据包。' + info: '

传输UDP数据包期间遇到的错误数.

'+ + 'RcvbufErrors - receive buffer is full. '+ + 'SndbufErrors - send buffer is full, no kernel memory available, or '+ + 'the IP layer reported an error when trying to send the packet and no error queue has been setup. '+ + 'InErrors - that is an aggregated counter for all errors, excluding NoPorts. '+ + 'NoPorts - no application is listening at the destination port. '+ + 'InCsumErrors - a UDP checksum failure is detected. '+ + 'IgnoredMulti - ignored multicast packets.' }, 'ipv4.udplite': { - info: '传输的UDP-Lite数据包的数量。' + info: '传输的UDP Lite数据包数.' }, 'ipv4.udplite_errors': { - info: '

传输UDP-Lite数据包时遇到的错误数量。

'+ - 'RcvbufErrors - 接收缓冲区已满。 '+ - 'SndbufErrors - 发送缓冲区已满,没有可用的内核内存,或 '+ - 'IP层在尝试发送数据包时报告了错误,并且没有设置错误队列。 '+ - 'InErrors - 这是所有错误的聚合计数器,不包括NoPorts。 '+ - 'NoPorts - 没有应用程序在目标端口监听。 '+ - 'InCsumErrors - 检测到UDP校验和失败。 '+ - '忽略多 - 忽略多播数据包。' + info: '

传输UDP Lite数据包期间遇到的错误数.

'+ + 'RcvbufErrors - receive buffer is full. '+ + 'SndbufErrors - send buffer is full, no kernel memory available, or '+ + 'the IP layer reported an error when trying to send the packet and no error queue has been setup. '+ + 'InErrors - that is an aggregated counter for all errors, excluding NoPorts. '+ + 'NoPorts - no application is listening at the destination port. '+ + 'InCsumErrors - a UDP checksum failure is detected. '+ + 'IgnoredMulti - ignored multicast packets.' }, 'ipv4.tcppackets': { - info: '

TCP层传输的数据包数量。

'+ - '

已收到-已收到的数据包,包括错误接收的数据包, '+ - '例如校验和错误、无效的TCP标头等。 '+ - '发送-发送数据包,不包括重新传输的数据包。 '+ - '但它包括SYN、ACK和RST数据包。

' + info: '

TCP层传输的数据包数.

'+ + '

Received - received packets, including those received in error, '+ + 'such as checksum error, invalid TCP header, and so on. '+ + 'Sent - sent packets, excluding the retransmitted packets. '+ + 'But it includes the SYN, ACK, and RST packets.

' }, 'ipv4.tcpsock': { - info: '当前状态已建立或关闭的TCP连接数量。 '+ - '这是测量时已建立连接的快照 '+ - '(即在同一迭代中建立连接和断开连接不会影响此指标)。' + info: '当前状态为“已建立”或“关闭等待”的TCP连接数. '+ + 'This is a snapshot of the established connections at the time of measurement '+ + '(i.e. a connection established and a connection disconnected within the same iteration will not affect this metric).' }, 'ipv4.tcpopens': { - info: '

TCP连接统计。

'+ - '

活跃 - 此主机尝试的传出TCP连接数量。 '+ - '被动 - 此主机接受的传入TCP连接数量。

' + info: '

TCP连接统计信息.

'+ + '

Active - number of outgoing TCP connections attempted by this host. '+ + 'Passive - number of incoming TCP connections accepted by this host.

' }, 'ipv4.tcperrors': { - info: '

TCP错误。

'+ - '

InErrs - 错误地收到 TCP 时段 '+ - '(包括标题太小、校验和错误、序列错误、错误数据包——适用于IPv4和IPv6)。 '+ - 'InCsumErrors - 收到校验和错误的TCP段(适用于IPv4和IPv6)。 '+ - 'RetransSegs - TCP段重新传输。

' + info: '

TCP错误.

'+ + '

InErrs - TCP segments received in error '+ + '(including header too small, checksum errors, sequence errors, bad packets - for both IPv4 and IPv6). '+ + 'InCsumErrors - TCP segments received with checksum errors (for both IPv4 and IPv6). '+ + 'RetransSegs - TCP segments retransmitted.

' }, 'ipv4.tcphandshake': { - info: '

TCP握手统计。

'+ - '

EstabResets - 已建立的连接重置 '+ - '(即从 ESTABLISHED 或 CLOSE_WAIT 直接过渡到 CLOSED 的连接)。 '+ - 'OutRsts - 发送TCP段,并设置RST标志(适用于IPv4和IPv6)。 '+ - 'AttemptFails - TCP连接从任一方向直接过渡的次数 '+ - 'SYN_SENT或SYN_RECV到CLOED,加上TCP连接直接过渡的次数 '+ - '从SYN_RECV到监听。 '+ - 'SynRetrans - 显示新的出站 TCP 连接的重试, '+ - '这可能表明远程主机上的一般连接问题或积压。

' + info: '

TCP握手统计信息.

'+ + '

EstabResets - established connections resets '+ + '(i.e. connections that made a direct transition from ESTABLISHED or CLOSE_WAIT to CLOSED). '+ + 'OutRsts - TCP segments sent, with the RST flag set (for both IPv4 and IPv6). '+ + 'AttemptFails - number of times TCP connections made a direct transition from either '+ + 'SYN_SENT or SYN_RECV to CLOSED, plus the number of times TCP connections made a direct transition '+ + 'from the SYN_RECV to LISTEN. '+ + 'SynRetrans - shows retries for new outbound TCP connections, '+ + 'which can indicate general connectivity issues or backlog on the remote host.

' }, 'ipv4.sockstat_sockets': { - info: '所有使用的套接字总数 '+ - '地址家庭'+ - '在这个系统中。' + info: '所有已用套接字的总数 '+ + 'address families '+ + 'in this system.' }, 'ipv4.sockstat_tcp_sockets': { - info: '

系统中某些TCP套接字的数量 '+ - 'states

'+ - '

Alloc - 处于任何 TCP 状态。 '+ - 'Orphan - 在任何用户进程中不再连接到套接字描述符, '+ - '但为了完成传输协议,内核仍然需要保持状态。 '+ - 'InUse - 处于任何 TCP 状态,TIME-WAIT 和 CLOSED 除外。 '+ - 'TimeWait - 处于TIME-WAIT状态。

' + info: '

系统中特定位置的TCP套接字数 '+ + 'states.

'+ + '

Alloc - in any TCP state. '+ + 'Orphan - no longer attached to a socket descriptor in any user processes, '+ + 'but for which the kernel is still required to maintain state in order to complete the transport protocol. '+ + 'InUse - in any TCP state, excluding TIME-WAIT and CLOSED. '+ + 'TimeWait - in the TIME-WAIT state.

' }, 'ipv4.sockstat_tcp_mem': { - info: '分配的TCP套接字使用的内存量。' + info: '分配的TCP套接字使用的内存量.' }, 'ipv4.sockstat_udp_sockets': { - info: '使用UDP套接字的数量。' + info: '使用的UDP套接字数.' }, 'ipv4.sockstat_udp_mem': { - info: '分配的UDP套接字使用的内存量。' + info: '分配的UDP套接字使用的内存量.' }, 'ipv4.sockstat_udplite_sockets': { - info: '使用UDP-Lite套接字的数量。' + info: '使用的UDP Lite套接字数.' }, 'ipv4.sockstat_raw_sockets': { - info: '使用原始套接字的数量。' + info: '使用的数量 采用原始套接字.' }, 'ipv4.sockstat_frag_sockets': { - info: '散列表中用于数据包重新组装的条目数量。' + info: '哈希表中用于数据包重组的条目数.' }, 'ipv4.sockstat_frag_mem': { - info: '用于数据包重新组装的内存量。' + info: '用于数据包重组的内存量.' }, // ------------------------------------------------------------------------ // IPv6 'ipv6.packets': { - info: '

此主机的IPv6数据包统计信息。

'+ - '

已收到 - IP 层接收的数据包。 '+ - '即使稍后删除数据包,这个计数器也会增加。 '+ - '发送-通过IP层发送的数据包,适用于单播和多播数据包。 '+ - '此计数器不包括转发中计算的任何数据包。 '+ - '转发 - 此主机不是其最终IP目的地的输入数据包, '+ - '结果,有人试图找到一条路线将他们转发到最终目的地。 '+ - '在不充当IP网关的主机中,此计数器将仅包括那些 '+ + info: '

此主机的IPv6数据包统计信息.

'+ + '

Received - IP层接收的数据包. '+ + 'This counter will be increased even if the packet is dropped later. '+ + 'Sent - packets sent via IP layer, for both single cast and multicast packets. '+ + 'This counter does not include any packets counted in Forwarded. '+ + 'Forwarded - input packets for which this host was not their final IP destination, '+ + 'as a result of which an attempt was made to find a route to forward them to that final destination. '+ + 'In hosts which do not act as IP Gateways, this counter will include only those packets which were '+ 'Source-Routed '+ - '源路由选项处理成功。 '+ - '交付 - 交付到上层协议的数据包,例如TCP、UDP、ICMP等。

' + 'and the Source-Route option processing was successful. '+ + 'Delivers - packets delivered to the upper layer protocols, e.g. TCP, UDP, ICMP, and so on.

' }, 'ipv6.fragsout': { - info: '

IPv6碎片'+ - '此系统的统计数据。

'+ - '

好的 - 已成功碎片化的数据包。 '+ - '失败 - 由于需要碎片化而被丢弃的数据包 '+ - '但不能,例如,由于Don\'t Fragment (DF)标志已设置。 '+ - '所有-碎片生成的碎片。

' + info: '

IPv6碎片 '+ + '此系统的统计信息.

'+ + '

OK - packets that have been successfully fragmented. '+ + 'Failed - packets that have been discarded because they needed to be fragmented '+ + 'but could not be, e.g. due to Don\'t Fragment (DF) flag was set. '+ + 'All - fragments that have been generated as a result of fragmentation.

' }, 'ipv6.fragsin': { info: '

IPv6重新组装 '+ - '此系统的统计数据。

'+ - '

好的 - 已成功重新组装的数据包。 '+ - '失败 - IP 重新组装算法检测到故障。 '+ - '这不一定是被丢弃的IP片段的计数,因为一些算法 '+ - '通过在收到碎片时进行组合,可能会丢失碎片数量。 '+ - '超时 - 检测到重新组装超时。 '+ - '所有-收到需要重新组装的IP片段。

' + '此系统的统计信息.

'+ + '

OK - packets that have been successfully reassembled. '+ + 'Failed - failures detected by the IP reassembly algorithm. '+ + 'This is not necessarily a count of discarded IP fragments since some algorithms '+ + 'can lose track of the number of fragments by combining them as they are received. '+ + 'Timeout - reassembly timeouts detected. '+ + 'All - received IP fragments which needed to be reassembled.

' }, 'ipv6.errors': { - info: '

丢弃的IPv6数据包的数量。

'+ - '

InDiscardsOutDiscards - 即使 '+ - '没有检测到错误来阻止它们交付到更高级别的协议。 '+ - 'InHdrErrors - IP头中的错误,包括糟糕的校验和、版本号不匹配、 '+ - '其他格式错误、超出使用时间等。 '+ - 'InAddrErrors - 无效的IP地址或目标IP地址不是本地地址,并且 '+ - '未启用IP转发。 '+ - 'InUnknownProtos - 未知或不支持的协议。 '+ - 'InTooBigErrors - 大小超过链接MTU。 '+ - 'InTruncatedPkts - 数据包框架没有携带足够的数据。 '+ - 'InNoRoutes - 转发时找不到任何路线。 '+ - 'OutNoRoutes - 找不到此主机生成的数据包的路由。

' + info: '

丢弃的IPv6数据包数.

'+ + '

InDiscards, OutDiscards - packets which were chosen to be discarded even though '+ + 'no errors had been detected to prevent their being deliverable to a higher-layer protocol. '+ + 'InHdrErrors - errors in IP headers, including bad checksums, version number mismatch, '+ + 'other format errors, time-to-live exceeded, etc. '+ + 'InAddrErrors - invalid IP address or the destination IP address is not a local address and '+ + 'IP forwarding is not enabled. '+ + 'InUnknownProtos - unknown or unsupported protocol. '+ + 'InTooBigErrors - the size exceeded the link MTU. '+ + 'InTruncatedPkts - packet frame did not carry enough data. '+ + 'InNoRoutes - no route could be found while forwarding. '+ + 'OutNoRoutes - no route could be found for packets generated by this host.

' }, 'ipv6.udppackets': { - info: '传输的UDP数据包的数量。' + info: '传输的UDP数据包数.' }, 'ipv6.udperrors': { - info: '

在传输UDP数据包时遇到的错误数量。

'+ - 'RcvbufErrors - 接收缓冲区已满。 '+ - 'SndbufErrors - 发送缓冲区已满,没有可用的内核内存,或 '+ - 'IP层在尝试发送数据包时报告了错误,并且没有设置错误队列。 '+ - 'InErrors - 这是所有错误的聚合计数器,不包括NoPorts。 '+ - 'NoPorts - 没有应用程序在目标端口监听。 '+ - 'InCsumErrors - 检测到UDP校验和失败。 '+ - '忽略多 - 忽略多播数据包。' + info: '

传输UDP数据包期间遇到的错误数.

'+ + 'RcvbufErrors - receive buffer is full. '+ + 'SndbufErrors - send buffer is full, no kernel memory available, or '+ + 'the IP layer reported an error when trying to send the packet and no error queue has been setup. '+ + 'InErrors - that is an aggregated counter for all errors, excluding NoPorts. '+ + 'NoPorts - no application is listening at the destination port. '+ + 'InCsumErrors - a UDP checksum failure is detected. '+ + 'IgnoredMulti - ignored multicast packets.' }, 'ipv6.udplitepackets': { - info: '传输的UDP-Lite数据包的数量。' + info: '传输的UDP Lite数据包数.' }, 'ipv6.udpliteerrors': { - info: '

传输UDP-Lite数据包时遇到的错误数量。

'+ - '

RcvbufErrors - 接收缓冲区已满。 '+ - 'SndbufErrors - 发送缓冲区已满,没有可用的内核内存,或 '+ - 'IP层在尝试发送数据包时报告了错误,并且没有设置错误队列。 '+ - 'InErrors - 这是所有错误的聚合计数器,不包括NoPorts。 '+ - 'NoPorts - 没有应用程序在目标端口监听。 '+ - 'InCsumErrors - 检测到UDP校验和失败。

' + info: '

传输UDP Lite数据包期间遇到的错误数.

'+ + '

RcvbufErrors - receive buffer is full. '+ + 'SndbufErrors - send buffer is full, no kernel memory available, or '+ + 'the IP layer reported an error when trying to send the packet and no error queue has been setup. '+ + 'InErrors - that is an aggregated counter for all errors, excluding NoPorts. '+ + 'NoPorts - no application is listening at the destination port. '+ + 'InCsumErrors - a UDP checksum failure is detected.

' }, 'ipv6.mcast': { - info: 'IPv6组播总流量。' + info: 'IPv6多播通信总量.' }, 'ipv6.bcast': { - info: 'IPv6广播总流量。' + info: 'IPv6广播总流量.' }, 'ipv6.mcastpkts': { - info: '传输的IPv6组播数据包总数。' + info: '传输的IPv6多播数据包总数.' }, 'ipv6.icmp': { - info: '

传输的ICMPv6消息数量。

'+ - '

收到发送-主机收到并试图发送的ICMP消息。 '+ - '这两个计数器都包含错误。

' + info: '

传输的ICMPv6消息数.

'+ + '

Received, Sent - ICMP messages which the host received and attempted to send. '+ + 'Both these counters include errors.

' }, 'ipv6.icmpredir': { - info: '传输的ICMPv6重定向消息的数量。'+ - '这些信息通知主机更新其路由信息(在替代路由上发送数据包)。' + info: '传输的ICMPv6重定向消息数. '+ + 'These messages inform a host to update its routing information (to send packets on an alternative route).' }, 'ipv6.icmpechos': { - info: 'ICMPv6回声消息的数量。' + info: '传输的ICMPv6重定向消息数.' }, 'ipv6.icmperrors': { - info: '

ICMPv6错误的数量和 '+ - '错误消息

'+ - '

InErrorsOutErrors - 糟糕的ICMP消息(错误的ICMP校验和,糟糕的长度等)。 '+ - 'InCsumErrors - 校验和错误。

' + info: '

ICMPv6错误数和 '+ + 'error messages.

'+ + '

InErrors, OutErrors - bad ICMP messages (bad ICMP checksums, bad length, etc.). '+ + 'InCsumErrors - wrong checksum.

' }, 'ipv6.groupmemb': { - info: '

传输的ICMPv6组成员消息的数量。

'+ - '

多播路由器发送组成员查询消息,以了解哪些组在其每个组上都有成员 '+ - '连接物理网络。主机计算机通过为每个 '+ - '主机加入的多播组。主机计算机也可以在以下情况下发送组成员报告 '+ - '它加入了一个新的多播组。 '+ - '当主机离开组播组时,会发送组成员减少消息。

' + info: '

传输的ICMPv6组成员身份消息数.

'+ + '

Multicast routers send Group Membership Query messages to learn which groups have members on each of their '+ + 'attached physical networks. Host computers respond by sending a Group Membership Report for each '+ + 'multicast group joined by the host. A host computer can also send a Group Membership Report when '+ + 'it joins a new multicast group. '+ + 'Group Membership Reduction messages are sent when a host computer leaves a multicast group.

' }, 'ipv6.icmprouter': { - info: '

转让ICMPv6的数量 '+ - '路由器发现消息。

'+ - '

路由器招标消息从计算机主机发送到局域网上的任何路由器 '+ - '要求他们在网络上做广告。 '+ - '路由器广告消息由局域网上的路由器发送,以宣布其IP地址 '+ - '可供路由。

' + info: '

传输的ICMPv6的数量 '+ + 'Router Discovery messages.

'+ + '

Router Solicitations message is sent from a computer host to any routers on the local area network '+ + 'to request that they advertise their presence on the network. '+ + 'Router Advertisement message is sent by a router on the local area network to announce its IP address '+ + 'as available for routing.

' }, 'ipv6.icmpneighbor': { - info: '

转让ICMPv6的数量 '+ - '邻居发现消息。

'+ - '

邻居请求被节点用于确定链接层地址 '+ - '邻居,或验证邻居是否仍然可以通过缓存的链接层地址访问。 '+ - '邻居广告被节点用于响应邻居邀约消息。

' + info: '

传输的ICMPv6的数量 '+ + 'Neighbour Discovery messages.

'+ + '

Neighbor Solicitations are used by nodes to determine the link layer address '+ + 'of a neighbor, or to verify that a neighbor is still reachable via a cached link layer address. '+ + 'Neighbor Advertisements are used by nodes to respond to a Neighbor Solicitation message.

' }, 'ipv6.icmpmldv2': { - info: '转让ICMPv6的数量'+ - '多播监听器发现(MLD)消息。' + info: '传输的ICMPv6的数量'+ + 'Multicast Listener Discovery (MLD) messages.' }, 'ipv6.icmptypes': { - info: '传输的ICMPv6消息数量 '+ - '某些类型。' + info: '已传输的ICMPv6消息数 '+ + 'certain types.' }, 'ipv6.ect': { - info: '

系统中设置了ECN位的接收IPv6数据包总数。

'+ - '

CEP - 遇到拥堵。 '+ - 'NoECTP - 不支持ECN的运输。 '+ - 'ECTP0ECTP1-支持ECN的传输。

' + info: '

系统中设置了ECN位的已接收IPv6数据包总数.

'+ + '

CEP - congestion encountered. '+ + 'NoECTP - non ECN-capable transport. '+ + 'ECTP0 and ECTP1 - ECN capable transport.

' }, 'ipv6.sockstat6_tcp_sockets': { - info: '任何TCP套接字的数量 '+ - 'state, '+ - '不包括时间等待和关闭。' + info: 'The number of TCP sockets in any '+ + 'state, '+ + 'excluding TIME-WAIT and CLOSED.' }, 'ipv6.sockstat6_udp_sockets': { - info: '使用UDP套接字的数量。' + info: '使用的UDP套接字数.' }, 'ipv6.sockstat6_udplite_sockets': { - info: '使用UDP-Lite套接字的数量。' + info: '使用的UDP Lite套接字数.' }, 'ipv6.sockstat6_raw_sockets': { - info: '使用原始套接字的数量。' + info: '使用的数量 raw sockets.' }, 'ipv6.sockstat6_frag_sockets': { - info: '散列表中用于数据包重新组装的条目数量。' + info: '哈希表中用于数据包重组的条目数.' }, @@ -2135,99 +2079,99 @@ netdataDashboard.context = { // SCTP 'sctp.established': { - info: '当前状态为的关联数量 '+ - '已建立、已关闭接收或即将关闭。' + info: 'The number of associations for which the current state is either '+ + 'ESTABLISHED, SHUTDOWN-RECEIVED or SHUTDOWN-PENDING.' }, 'sctp.transitions': { - info: '

关联在州之间直接过渡的次数。

'+ - '

活跃 - 从COOKIE-ECHOED到已建立。上层发起了关联尝试。 '+ - '被动 - 从关闭到已建立。远程端点发起了关联尝试。 '+ - '中止-使用原始ABORT从任何状态到关闭。不光彩地终止协会。 '+ - 'Shutdown - 从SHUTDOWN-SENT或SHUTDOWN-ACK-SENT到CLOSHED。优雅地终止协会。

' + info: '

协会在国家间直接过渡的次数.

'+ + '

Active - from COOKIE-ECHOED to ESTABLISHED. The upper layer initiated the association attempt. '+ + 'Passive - from CLOSED to ESTABLISHED. The remote endpoint initiated the association attempt. '+ + 'Aborted - from any state to CLOSED using the primitive ABORT. Ungraceful termination of the association. '+ + 'Shutdown - from SHUTDOWN-SENT or SHUTDOWN-ACK-SENT to CLOSED. Graceful termination of the association.

' }, 'sctp.packets': { - info: '

传输的SCTP数据包数量。

'+ - '

已收到 - 包含重复的数据包。 '+ - '发送-包括重新传输的数据块。

' + info: '

传输的SCTP数据包数.

'+ + '

Received - includes duplicate packets. '+ + 'Sent - includes retransmitted DATA chunks.

' }, 'sctp.packet_errors': { - info: '

接收SCTP数据包时遇到的错误数量。

'+ - '

无效 - 接收方无法识别适当关联的数据包。 '+ - '校验和 - 校验和无效的数据包。

' + info: '

接收SCTP数据包期间遇到的错误数.

'+ + '

Invalid - packets for which the receiver was unable to identify an appropriate association. '+ + 'Checksum - packets with an invalid checksum.

' }, 'sctp.fragmentation': { - info: '

碎片化和重新组装的SCTP消息的数量。

'+ - '

重新组装 - 重新组装用户消息,在转换为数据块后。 '+ - '碎片化 - 由于MTU而不得不碎片化的用户消息。

' + info: '

分段和重新组合的SCTP消息数.

'+ + '

Reassembled - reassembled user messages, after conversion into DATA chunks. '+ + 'Fragmented - user messages that have to be fragmented because of the MTU.

' }, 'sctp.chunks': { - info: '传输控件、有序和无顺序数据块的数量。 '+ - '不包括重播和重复。' + info: '传输的控制、有序和无序数据块的数量. '+ + 'Retransmissions and duplicates are not included.' }, // ------------------------------------------------------------------------ // Netfilter Connection Tracker 'netfilter.conntrack_sockets': { - info: 'Conntrack表中的条目数。' + info: 'conntrack表中的条目数.' }, 'netfilter.conntrack_new': { - info: '

数据包跟踪统计信息。(自v4.9以来)和忽略(自v5.10以来)在最新内核中被硬编码为零。

'+ - '

- 添加以前意想不到的条目。 '+ - '忽略-已连接到conntrack条目的数据包。 '+ - '无效 - 看到无法跟踪的数据包。

' + info: '

数据包跟踪统计信息. New (since v4.9) and Ignore (since v5.10) are hardcoded to zeros in the latest kernel.

'+ + '

New - conntrack entries added which were not expected before. '+ + 'Ignore - packets seen which are already connected to a conntrack entry. '+ + 'Invalid - packets seen which can not be tracked.

' }, 'netfilter.conntrack_changes': { - info: '

conntrack表格中的更改数量。

'+ - '

插入删除-跟踪插入或删除的条目。 '+ - '删除列表 - 跟踪被列入垂死列表的条目。

' + info: '

conntrack表中的更改数.

'+ + '

Inserted, Deleted - conntrack entries which were inserted or removed. '+ + 'Delete-list - conntrack entries which were put to dying list.

' }, 'netfilter.conntrack_expect': { - info: '

“预期”表中的事件数量。 '+ - '连接跟踪预期是用于“预期”与现有连接相关连接的机制。 '+ - '期望是预计在一段时间内发生的连接。

'+ - '

创建删除-跟踪插入或删除的条目。 '+ - ' - 在对它们的预期已经存在后添加了conntrack条目。

' + info: '

The number of events in the "expect" table. '+ + 'Connection tracking expectations are the mechanism used to "expect" RELATED connections to existing ones. '+ + 'An expectation is a connection that is expected to happen in a period of time.

'+ + '

Created, Deleted - conntrack entries which were inserted or removed. '+ + 'New - conntrack entries added after an expectation for them was already present.

' }, 'netfilter.conntrack_search': { - info: '

Conntrack表查找统计信息。

'+ - '

Searched - 进行conntrack 表格查找。 '+ - '重新启动-由于散列调整大小而不得不重新启动的conntrack表查找。 '+ - '找到-成功跟踪表格查找。

' + info: '

Conntrack表查找统计信息.

'+ + '

Searched - conntrack table lookups performed. '+ + 'Restarted - conntrack table lookups which had to be restarted due to hashtable resizes. '+ + 'Found - conntrack table lookups which were successful.

' }, 'netfilter.conntrack_errors': { - info: '

Conntrack错误。

'+ - '

IcmpError - 由于错误情况无法跟踪的数据包。 '+ - '插入失败 - 尝试插入列表但失败的条目 '+ - '(如果同一条目已经存在,则可能)。 '+ - 'Drop - 由于conntrack失败而删除的数据包。 '+ - '要么新的conntrack条目分配失败,要么协议帮助程序删除数据包。 '+ - 'EarlyDrop - 如果达到最大表大小,请删除conntrack条目,为新条目腾出空间。

' + info: '

Conntrack错误.

'+ + '

IcmpError - packets which could not be tracked due to error situation. '+ + 'InsertFailed - entries for which list insertion was attempted but failed '+ + '(happens if the same entry is already present). '+ + 'Drop - packets dropped due to conntrack failure. '+ + 'Either new conntrack entry allocation failed, or protocol helper dropped the packet. '+ + 'EarlyDrop - dropped conntrack entries to make room for new ones, if maximum table size was reached.

' }, 'netfilter.synproxy_syn_received': { - info: '从客户端收到的初始TCP SYN数据包的数量。' + info: '从客户端接收的初始TCP SYN数据包数.' }, 'netfilter.synproxy_conn_reopened': { - info: '直接从TIME-WAIT状态由新的TCP SYN数据包重新打开连接的数量。' + info: '新TCP SYN数据包直接从TIME-WAIT状态重新打开的连接数.' }, 'netfilter.synproxy_cookies': { - info: '

SYNPROXY Cookie统计。

'+ - '

有效无效-从客户端收到的TCP ACK数据包中的cookie验证结果。 '+ - '重新传输 - TCP SYN数据包重新传输到服务器。 '+ - '当客户端重复TCP ACK且与服务器的连接尚未建立时,就会发生这种情况。

' + info: '

SYNPROXY cookie统计信息.

'+ + '

Valid, Invalid - result of cookie validation in TCP ACK packets received from clients. '+ + 'Retransmits - TCP SYN packets retransmitted to the server. '+ + 'It happens when the client repeats TCP ACK and the connection to the server is not yet established.

' }, // ------------------------------------------------------------------------ @@ -2235,414 +2179,414 @@ netdataDashboard.context = { // APPS cpu 'apps.cpu': { - info: 'CPU总利用率(所有内核)。它包括用户、系统和客人时间。' + info: 'Total CPU utilization (all cores). It includes user, system and guest time.' }, 'groups.cpu': { - info: 'CPU总利用率(所有内核)。它包括用户、系统和客人时间。' + info: 'Total CPU utilization (all cores). It includes user, system and guest time.' }, 'users.cpu': { - info: 'CPU总利用率(所有内核)。它包括用户、系统和客人时间。' + info: 'Total CPU utilization (all cores). It includes user, system and guest time.' }, 'apps.cpu_user': { - info: 'CPU 忙于执行代码所需的时间 '+ - '用户模式(所有核心)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'user mode (all cores).' }, 'groups.cpu_user': { - info: 'CPU 忙于执行代码所需的时间 '+ - '用户模式(所有核心)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'user mode (all cores).' }, 'users.cpu_user': { - info: 'TCPU忙于执行代码的大量时间 '+ - '用户模式(所有核心)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'user mode (all cores).' }, 'apps.cpu_system': { - info: 'CPU 忙于执行代码所需的时间 '+ - '内核模式(所有内核)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'kernel mode (all cores).' }, 'groups.cpu_system': { - info: 'CPU 忙于执行代码所需的时间 '+ - '内核模式(所有内核)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'kernel mode (all cores).' }, 'users.cpu_system': { - info: 'CPU 忙于执行代码所需的时间 '+ - '内核模式(所有内核)。' + info: 'The amount of time the CPU was busy executing code in '+ + 'kernel mode (all cores).' }, 'apps.cpu_guest': { - info: '为来宾操作系统(所有内核)运行虚拟CPU所花费的时间。' + info: 'The amount of time spent running a virtual CPU for a guest operating system (all cores).' }, 'groups.cpu_guest': { - info: '为来宾操作系统(所有内核)运行虚拟CPU所花费的时间。' + info: 'The amount of time spent running a virtual CPU for a guest operating system (all cores).' }, 'users.cpu_guest': { - info: '为来宾操作系统(所有内核)运行虚拟CPU所花费的时间。' + info: 'The amount of time spent running a virtual CPU for a guest operating system (all cores).' }, // APPS disk 'apps.preads': { - info: '从存储层读取的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been read from the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'groups.preads': { - info: '从存储层读取的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been read from the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'users.preads': { - info: '从存储层读取的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been read from the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'apps.pwrites': { - info: '已写入存储层的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written to the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'groups.pwrites': { - info: '已写入存储层的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written to the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'users.pwrites': { - info: '已写入存储层的数据量。 '+ - '需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written to the storage layer. '+ + 'Actual physical disk I/O was required.' }, 'apps.lreads': { - info: '从存储层读取的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是实际的物理磁盘I/O是必需的 '+ - '(读数可能已从pagecache中满意)。' + info: 'The amount of data that has been read from the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required '+ + '(the read might have been satisfied from pagecache).' }, 'groups.lreads': { - info: '从存储层读取的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是实际的物理磁盘I/O是必需的 '+ - '(读数可能已从pagecache中满意)。' + info: 'The amount of data that has been read from the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required '+ + '(the read might have been satisfied from pagecache).' }, 'users.lreads': { - info: '从存储层读取的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是实际的物理磁盘I/O是必需的 '+ - '(读数可能已从pagecache中满意)。' + info: 'The amount of data that has been read from the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required '+ + '(the read might have been satisfied from pagecache).' }, 'apps.lwrites': { - info: '已写入或应写入存储层的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written or shall be written to the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required.' }, 'groups.lwrites': { - info: '已写入或应写入存储层的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written or shall be written to the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required.' }, 'users.lwrites': { - info: '已写入或应写入存储层的数据量。 '+ - '它包括I/O终端等内容,不受是否或 '+ - '不是需要实际的物理磁盘I/O。' + info: 'The amount of data that has been written or shall be written to the storage layer. '+ + 'It includes things such as terminal I/O and is unaffected by whether or '+ + 'not actual physical disk I/O was required.' }, 'apps.files': { - info: '打开的文件和目录的数量。' + info: 'The number of open files and directories.' }, 'groups.files': { - info: '打开的文件和目录的数量。' + info: 'The number of open files and directories.' }, 'users.files': { - info: '打开的文件和目录的数量。' + info: 'The number of open files and directories.' }, // APPS mem 'apps.mem': { - info: '应用程序使用的真实内存(RAM)。这不包括共享内存。' + info: 'Real memory (RAM) used by applications. This does not include shared memory.' }, 'groups.mem': { - info: '每个用户组使用的真实内存(RAM)。这不包括共享内存。' + info: 'Real memory (RAM) used per user group. This does not include shared memory.' }, 'users.mem': { - info: '每个用户组使用的真实内存(RAM)。这不包括共享内存。' + info: 'Real memory (RAM) used per user group. This does not include shared memory.' }, 'apps.vmem': { - info: '由应用程序分配的虚拟内存。 '+ - '有关更多信息,请查看本文。' + info: 'Virtual memory allocated by applications. '+ + 'Check this article for more information.' }, 'groups.vmem': { - info: '自Netdata重新启动以来,每个用户组分配的虚拟内存。有关更多信息,请查看本文。' + info: 'Virtual memory allocated per user group since the Netdata restart. Please check this article for more information.' }, 'users.vmem': { - info: '自Netdata重新启动以来,每个用户组分配的虚拟内存。有关更多信息,请查看本文。' + info: 'Virtual memory allocated per user group since the Netdata restart. Please check this article for more information.' }, 'apps.minor_faults': { - info: '小故障的数量 '+ - '不需要从磁盘加载内存页面。 '+ - '当一个进程需要内存中的数据并分配给另一个进程时,会出现轻微的页面故障。 '+ - '他们在多个进程之间共享内存页面—— '+ - '无需将其他数据从磁盘读取到内存。' + info: 'The number of minor faults '+ + 'which have not required loading a memory page from the disk. '+ + 'Minor page faults occur when a process needs data that is in memory and is assigned to another process. '+ + 'They share memory pages between multiple processes – '+ + 'no additional data needs to be read from disk to memory.' }, 'groups.minor_faults': { - info: '小故障的数量 '+ - '不需要从磁盘加载内存页面。 '+ - '当一个进程需要内存中的数据并分配给另一个进程时,会出现轻微的页面故障。 '+ - '他们在多个进程之间共享内存页面—— '+ - '无需将其他数据从磁盘读取到内存。' + info: 'The number of minor faults '+ + 'which have not required loading a memory page from the disk. '+ + 'Minor page faults occur when a process needs data that is in memory and is assigned to another process. '+ + 'They share memory pages between multiple processes – '+ + 'no additional data needs to be read from disk to memory.' }, 'users.minor_faults': { - info: '小故障的数量 '+ - '不需要从磁盘加载内存页面。 '+ - '当一个进程需要内存中的数据并分配给另一个进程时,会出现轻微的页面故障。 '+ - '他们在多个进程之间共享内存页面——'+ - '无需将其他数据从磁盘读取到内存。' + info: 'The number of minor faults '+ + 'which have not required loading a memory page from the disk. '+ + 'Minor page faults occur when a process needs data that is in memory and is assigned to another process. '+ + 'They share memory pages between multiple processes – '+ + 'no additional data needs to be read from disk to memory.' }, // APPS processes 'apps.threads': { - info: '线程的数量。' + info: 'The number of threads.' }, 'groups.threads': { - info: '线程的数量。' + info: 'The number of threads.' }, 'users.threads': { - info: '线程的数量。' + info: 'The number of threads.' }, 'apps.processes': { - info: '进程的数量。' + info: 'The number of processes.' }, 'groups.processes': { - info: '进程的数量。' + info: 'The number of processes.' }, 'users.processes': { - info: '进程的数量。' + info: 'The number of processes.' }, 'apps.uptime': { - info: '组中至少一个进程运行的时间段。' + info: 'The period of time within which at least one process in the group has been running.' }, 'groups.uptime': { - info: '组中至少一个进程运行的时间段。' + info: 'The period of time within which at least one process in the group has been running.' }, 'users.uptime': { - info: '组中至少一个进程运行的时间段。' + info: 'The period of time within which at least one process in the group has been running.' }, 'apps.uptime_min': { - info: '组中进程中最短的正常运行时间。' + info: 'The shortest uptime among processes in the group.' }, 'groups.uptime_min': { - info: '组中进程中最短的正常运行时间。' + info: 'The shortest uptime among processes in the group.' }, 'users.uptime_min': { - info: '组中进程中最短的正常运行时间。' + info: 'The shortest uptime among processes in the group.' }, 'apps.uptime_avg': { - info: '组中进程的平均正常运行时间。' + info: 'The average uptime of processes in the group.' }, 'groups.uptime_avg': { - info: '组中进程的平均正常运行时间。' + info: 'The average uptime of processes in the group.' }, 'users.uptime_avg': { - info: '组中进程的平均正常运行时间。' + info: 'The average uptime of processes in the group.' }, 'apps.uptime_max': { - info: '组中进程中最长的正常运行时间。' + info: 'The longest uptime among processes in the group.' }, 'groups.uptime_max': { - info: '组中进程中最长的正常运行时间。' + info: 'The longest uptime among processes in the group.' }, 'users.uptime_max': { - info: '组中进程中最长的正常运行时间。' + info: 'The longest uptime among processes in the group.' }, 'apps.pipes': { - info: '开放数量 '+ - '管道。 '+ - '管道是一种单向数据通道,可用于进程间通信。' + info: 'The number of open '+ + 'pipes. '+ + 'A pipe is a unidirectional data channel that can be used for interprocess communication.' }, 'groups.pipes': { - info: '开放数量 '+ - '管道。 '+ - '管道是一种单向数据通道,可用于进程间通信。' + info: 'The number of open '+ + 'pipes. '+ + 'A pipe is a unidirectional data channel that can be used for interprocess communication.' }, 'users.pipes': { - info: '开放数量 '+ - '管道。 '+ - '管道是一种单向数据通道,可用于进程间通信。' + info: 'The number of open '+ + 'pipes. '+ + 'A pipe is a unidirectional data channel that can be used for interprocess communication.' }, // APPS swap 'apps.swap': { - info: '匿名私人页面交换虚拟内存的数量。 '+ - '这不包括共享交换内存。' + info: 'The amount of swapped-out virtual memory by anonymous private pages. '+ + 'This does not include shared swap memory.' }, 'groups.swap': { - info: '匿名私人页面交换虚拟内存的数量。 '+ - '这不包括共享交换内存。' + info: 'The amount of swapped-out virtual memory by anonymous private pages. '+ + 'This does not include shared swap memory.' }, 'users.swap': { - info: '匿名私人页面交换虚拟内存的数量。 '+ - '这不包括共享交换内存。' + info: 'The amount of swapped-out virtual memory by anonymous private pages. '+ + 'This does not include shared swap memory.' }, 'apps.major_faults': { - info: '重大故障的数量 '+ - '需要从磁盘加载内存页面。 '+ - '由于RAM中缺少所需的页面,会出现重大页面故障。 '+ - '当流程开始或需要读取其他数据时,它们是正常的 '+ - '在这些情况下,不表示问题状况。 '+ - '然而,一个主要的页面错误也可能是阅读已写出的内存页面的结果 '+ - '交换文件,这可能表明内存短缺。' + info: 'The number of major faults '+ + 'which have required loading a memory page from the disk. '+ + 'Major page faults occur because of the absence of the required page from the RAM. '+ + 'They are expected when a process starts or needs to read in additional data and '+ + 'in these cases do not indicate a problem condition. '+ + 'However, a major page fault can also be the result of reading memory pages that have been written out '+ + 'to the swap file, which could indicate a memory shortage.' }, 'groups.major_faults': { - info: '重大故障的数量 '+ - '需要从磁盘加载内存页面。 '+ - '由于RAM中缺少所需的页面,会出现重大页面故障。 '+ - '当流程开始或需要读取其他数据时,它们是正常的 '+ - '在这些情况下,不表示问题状况。 '+ - '然而,一个主要的页面错误也可能是阅读已写出的内存页面的结果 '+ - '交换文件,这可能表明内存短缺。' + info: 'The number of major faults '+ + 'which have required loading a memory page from the disk. '+ + 'Major page faults occur because of the absence of the required page from the RAM. '+ + 'They are expected when a process starts or needs to read in additional data and '+ + 'in these cases do not indicate a problem condition. '+ + 'However, a major page fault can also be the result of reading memory pages that have been written out '+ + 'to the swap file, which could indicate a memory shortage.' }, 'users.major_faults': { - info: '重大故障的数量 '+ - '需要从磁盘加载内存页面。 '+ - '由于RAM中缺少所需的页面,会出现重大页面故障。 '+ - '当流程开始或需要读取其他数据时,它们是正常的 '+ - '在这些情况下,不表示问题状况。 '+ - '然而,一个主要的页面错误也可能是阅读已写出的内存页面的结果 '+ - '交换文件,这可能表明内存短缺。' + info: 'The number of major faults '+ + 'which have required loading a memory page from the disk. '+ + 'Major page faults occur because of the absence of the required page from the RAM. '+ + 'They are expected when a process starts or needs to read in additional data and '+ + 'in these cases do not indicate a problem condition. '+ + 'However, a major page fault can also be the result of reading memory pages that have been written out '+ + 'to the swap file, which could indicate a memory shortage.' }, // APPS net 'apps.sockets': { - info: '打开sockets的数量。 '+ - 'sockets是一种在服务器上运行的程序之间实现进程间通信的方式, '+ - '或在不同服务器上运行的程序之间。这包括网络和UNIX sockets。' + info: 'The number of open sockets. '+ + 'Sockets are a way to enable inter-process communication between programs running on a server, '+ + 'or between programs running on separate servers. This includes both network and UNIX sockets.' }, 'groups.sockets': { - info: '打开sockets的数量。 '+ - 'sockets是一种在服务器上运行的程序之间实现进程间通信的方式, '+ - '或在不同服务器上运行的程序之间。这包括网络和UNIX sockets。' + info: 'The number of open sockets. '+ + 'Sockets are a way to enable inter-process communication between programs running on a server, '+ + 'or between programs running on separate servers. This includes both network and UNIX sockets.' }, 'users.sockets': { - info: '打开sockets的数量 '+ - 'sockets是一种在服务器上运行的程序之间实现进程间通信的方式, '+ - '或在不同服务器上运行的程序之间。这包括网络和UNIX sockets。' + info: 'The number of open sockets. '+ + 'Sockets are a way to enable inter-process communication between programs running on a server, '+ + 'or between programs running on separate servers. This includes both network and UNIX sockets.' }, // Apps eBPF stuff 'apps.file_open': { - info: '对内部函数do_sys_open的调用(对于比5.5.19更新的内核,我们在do_sys_openat2中添加一个kprobe。),这是从' + + info: 'Calls to the internal function do_sys_open (for kernels newer than 5.5.19 we add a kprobe to do_sys_openat2. ), which is the common function called from' + ' open(2) ' + ' and openat(2). ' }, 'apps.file_open_error': { - info: '对内部函数do_sys_open的调用失败(对于比5.5.19更新的内核,我们向do_sys_openat2添加了一个kprobe。)。' + info: 'Failed calls to the internal function do_sys_open (for kernels newer than 5.5.19 we add a kprobe to do_sys_openat2. ).' }, 'apps.file_closed': { - info: '根据内核版本调用内部函数__close_fdclose_fd,该版本调用' + + info: 'Calls to the internal function __close_fd or close_fd according to your kernel version, which is called from' + ' close(2). ' }, 'apps.file_close_error': { - info: '根据内核版本,对内部函数__close_fdclose_fd的调用失败。' + info: 'Failed calls to the internal function __close_fd or close_fd according to your kernel version.' }, 'apps.file_deleted': { - info: '调用函数vfs_unlink。此图表没有显示从文件系统中删除文件的所有事件,因为文件系统可以创建自己的功能来删除文件。' + info: 'Calls to the function vfs_unlink. This chart does not show all events that remove files from the filesystem, because filesystems can create their own functions to remove files.' }, 'apps.vfs_write_call': { - info: '成功调用了函数vfs_write。如果此图表使用其他功能将数据存储在磁盘上,则可能不会显示所有文件系统事件。' + info: 'Successful calls to the function vfs_write. This chart may not show all filesystem events if it uses other functions to store data on disk.' }, 'apps.vfs_write_error': { - info: '对函数vfs_write的调用失败。如果此图表使用其他功能将数据存储在磁盘上,则可能不会显示所有文件系统事件。' + info: 'Failed calls to the function vfs_write. This chart may not show all filesystem events if it uses other functions to store data on disk.' }, 'apps.vfs_read_call': { - info: '成功调用函数vfs_read。如果此图表使用其他功能将数据存储在磁盘上,则可能不会显示所有文件系统事件。' + info: 'Successful calls to the function vfs_read. This chart may not show all filesystem events if it uses other functions to store data on disk.' }, 'apps.vfs_read_error': { - info: '对函数vfs_read的调用失败。如果此图表使用其他功能将数据存储在磁盘上,则可能不会显示所有文件系统事件。' + info: 'Failed calls to the function vfs_read. This chart may not show all filesystem events if it uses other functions to store data on disk.' }, 'apps.vfs_write_bytes': { - info: '使用函数vfs_write成功编写的字节总数。' + info: 'Total of bytes successfully written using the function vfs_write.' }, 'apps.vfs_read_bytes': { - info: '使用函数vfs_read成功读取的总字节总数。' + info: 'Total of bytes successfully read using the function vfs_read.' }, 'apps.process_create': { - info: '调用do_fork,或者kernel_clone(如果您运行的内核更新于5.16),以创建一个新任务,这是用于定义内核内进程和任务的常用名称。此图表由eBPF插件提供。' + info: 'Calls to either do_fork, or kernel_clone if you are running kernel newer than 5.9.16, to create a new task, which is the common name used to define process and tasks inside the kernel. This chart is provided by eBPF plugin.' }, 'apps.thread_create': { - info: '调用do_fork,或者kernel_clone(如果您运行的内核更新于5.16),以创建一个新任务,这是用于定义内核内进程和任务的常用名称。Netdata标识监控跟踪点sched_process_fork的线程。此图表由eBPF插件提供。' + info: 'Calls to either do_fork, or kernel_clone if you are running kernel newer than 5.9.16, to create a new task, which is the common name used to define process and tasks inside the kernel. Netdata identifies the threads monitoring tracepoint sched_process_fork. This chart is provided by eBPF plugin.' }, 'apps.task_exit': { - info: '对负责关闭的函数的调用(do_exit)任务。此图表由eBPF插件提供。' + info: 'Calls to the function responsible for closing (do_exit) tasks. This chart is provided by eBPF plugin.' }, 'apps.task_close': { - info: '对负责发布功能的调用(release_task)任务。此图表由eBPF插件提供。' + info: 'Calls to the function responsible for releasing (release_task) tasks. This chart is provided by eBPF plugin.' }, 'apps.task_error': { - info: '创建新进程或线程的错误数量。此图表由eBPF插件提供。' + info: 'Number of errors to create a new process or thread. This chart is provided by eBPF plugin.' }, 'apps.total_bandwidth_sent': { - info: '由函数tcp_sendmsgudp_sendmsg发送的字节。' + info: 'Bytes sent by functions tcp_sendmsg and udp_sendmsg.' }, 'apps.total_bandwidth_recv': { - info: '函数tcp_cleanup_rbufudp_recvmsg收到的字节。我们使用tcp_cleanup_rbuf而不是tcp_recvmsg,因为这最后错过了tcp_read_sock()流量,我们还需要有更多的探针来获取套接字和包大小。' + info: 'Bytes received by functions tcp_cleanup_rbuf and udp_recvmsg. We use tcp_cleanup_rbuf instead tcp_recvmsg, because this last misses tcp_read_sock() traffic and we would also need to have more probes to get the socket and package size.' }, 'apps.bandwidth_tcp_send': { - info: '函数tcp_sendmsg用于收集从TCP连接发送的字节数。' + info: 'The function tcp_sendmsg is used to collect number of bytes sent from TCP connections.' }, 'apps.bandwidth_tcp_recv': { - info: 'tcp_cleanup_rbuf函数用于收集从TCP连接接收的字节数。' + info: 'The function tcp_cleanup_rbuf is used to collect number of bytes received from TCP connections.' }, 'apps.bandwidth_tcp_retransmit': { - info: '当主机没有收到发送的数据包的预期返回时,将调用函数tcp_retransmit_skb。' + info: 'The function tcp_retransmit_skb is called when the host did not receive the expected return from a packet sent.' }, 'apps.bandwidth_udp_send': { - info: 'udp_sendmsg函数用于收集从UDP连接发送的字节数。' + info: 'The function udp_sendmsg is used to collect number of bytes sent from UDP connections.' }, 'apps.bandwidth_udp_recv': { - info: '函数udp_recvmsg用于收集从UDP连接接收的字节数。' + info: 'The function udp_recvmsg is used to collect number of bytes received from UDP connections.' }, 'apps.dc_hit_ratio': { - info: '目录缓存中存在的文件访问百分比。100%表示访问的每个文件都存在于目录缓存中。如果目录缓存中不存在文件1)它们不存在于文件系统中,2)以前没有访问过文件。阅读更多关于目录缓存的信息。Netdata还在文件系统子菜单中对这些图表进行了摘要。' + info: 'Percentage of file accesses that were present in the directory cache. 100% means that every file that was accessed was present in the directory cache. If files are not present in the directory cache 1) they are not present in the file system, 2) the files were not accessed before. Read more about directory cache. Netdata also gives a summary for these charts in Filesystem submenu.' }, 'apps.dc_reference': { - info: '文件访问计数器。引用是文件访问时,请参阅filesystem.dc_reference图表以了解更多上下文。阅读更多关于目录缓存的信息。' + info: 'Counters of file accesses. Reference is when there is a file access, see the filesystem.dc_reference chart for more context. Read more about directory cache.' }, 'apps.dc_not_cache': { - info: '文件访问计数器。是指有文件访问且目录缓存中不存在文件时,请参阅filesystem.dc_reference图表以了解更多上下文。阅读更多关于目录缓存的信息。' + info: 'Counters of file accesses. Slow is when there is a file access and the file is not present in the directory cache, see the filesystem.dc_reference chart for more context. Read more about directory cache.' }, 'apps.dc_not_found': { - info: '文件访问计数器。Miss是当有文件访问且文件系统中找不到文件时,请参阅filesystem.dc_reference图表以获取更多上下文。阅读更多关于目录缓存的信息。' + info: 'Counters of file accesses. Miss is when there is file access and the file is not found in the filesystem, see the filesystem.dc_reference chart for more context. Read more about directory cache.' }, // ------------------------------------------------------------------------ @@ -2709,20 +2653,20 @@ netdataDashboard.context = { return ''; } ], - info: '网络接口传输的流量。' + info: 'The amount of traffic transferred by the network interface.' }, 'net.packets': { - info: '网络接口传输的数据包数量。 '+ - '收到的multicast计数器是 '+ - '通常在设备级别计算(与接收不同),因此可能包括未到达主机的数据包。' + info: 'The number of packets transferred by the network interface. '+ + 'Received multicast counter is '+ + 'commonly calculated at the device level (unlike received) and therefore may include packets which did not reach the host.' }, 'net.errors': { - info: '

网络接口遇到的错误数量。

'+ - '

入站 - 此界面上收到的不良数据包。 '+ - '它包括因长度无效、CRC、帧对齐和其他错误而掉落的数据包。 '+ - '出站 - 传输问题。 '+ - '它包括因运营商丢失而导致的帧传输错误、FIFO超支/下流、心跳、 '+ - '延迟碰撞和其他问题。

' + info: '

The number of errors encountered by the network interface.

'+ + '

Inbound - bad packets received on this interface. '+ + 'It includes dropped packets due to invalid length, CRC, frame alignment, and other errors. '+ + 'Outbound - transmit problems. '+ + 'It includes frames transmission errors due to loss of carrier, FIFO underrun/underflow, heartbeat, '+ + 'late collisions, and other problems.

' }, 'net.fifo': { info: '

The number of FIFO errors encountered by the network interface.

'+ @@ -2909,7 +2853,7 @@ netdataDashboard.context = { }, 'disk_ext.io': { - info: 'The amount of discarded data that are no longer in use by a mounted file system.' + info: '已装入的文件系统不再使用的丢弃数据量.' }, 'disk.ops': { @@ -2962,7 +2906,7 @@ netdataDashboard.context = { }, 'disk_ext.avgsz': { height: 0.5, - info: 'The average discard operation size.' + info: '平均丢弃操作大小.' }, 'disk.await': { height: 0.5, @@ -3674,7 +3618,7 @@ netdataDashboard.context = { + ' role="application">'; } ], - info: 'Total CPU utilization within the configured or system-wide (if not set) limits. '+ + info: 'CPU总利用率在配置或系统范围(如果未设置)限制内。 '+ 'When the CPU utilization of a cgroup exceeds the limit for the configured period, '+ 'the tasks belonging to its hierarchy will be throttled and are not allowed to run again until the next period.' }, @@ -3699,7 +3643,7 @@ netdataDashboard.context = { return ''; } ], - info: 'Total CPU utilization within the system-wide CPU resources (all cores). '+ + info: '系统范围CPU资源(所有内核)内的总CPU利用率。 '+ 'The amount of time spent by tasks of the cgroup in '+ 'user and kernel modes.' }, @@ -6353,57 +6297,57 @@ netdataDashboard.context = { }, 'systemd.scope_unit_state': { - info: '切片单元可用于对管理系统流程的单元进行分组(如服务和范围单元) ' + - '在用于资源管理的分层树中。 ' + - '请参阅 systemd.scope(5)。' + info: 'Slice units may be used to group units which manage system processes (such as service and scope units) ' + + 'in a hierarchical tree for resource management purposes. ' + + 'See systemd.scope(5).' }, 'systemd.slice_unit_state': { - info: '范围单位与服务单位相似,但也管理外国流程,而不是启动它们。 ' + - '请参阅 systemd.slice(5)。' + info: 'Scope units are similar to service units, but manage foreign processes instead of starting them as well. ' + + 'See systemd.slice(5).' }, 'anomaly_detection.dimensions': { - info: '被认为异常或正常的维度总数。 ' + info: 'Total count of dimensions considered anomalous or normal. ' }, 'anomaly_detection.anomaly_rate': { - info: '异常维度的百分比。 ' + info: 'Percentage of anomalous dimensions. ' }, 'anomaly_detection.detector_window': { - info: '探测器使用的有源窗口的长度。 ' + info: 'The length of the active window used by the detector. ' }, 'anomaly_detection.detector_events': { - info: '标志(0或1),用于显示探测器何时触发异常事件。 ' + info: 'Flags (0 or 1) to show when an anomaly event has been triggered by the detector. ' }, 'anomaly_detection.prediction_stats': { - info: '与异常检测预测时间相关的诊断指标。 ' + info: 'Diagnostic metrics relating to prediction time of anomaly detection. ' }, 'anomaly_detection.training_stats': { - info: '与异常检测培训时间相关的诊断指标。 ' + info: 'Diagnostic metrics relating to training time of anomaly detection. ' }, // ------------------------------------------------------------------------ // Supervisor 'fail2ban.failed_attempts': { - info: '

尝试失败的次数。

'+ - '

此图表反映了\'Found\'行的数量。 '+ - '找到意味着服务日志文件中的一行与其过滤器中的失败正则表达式匹配。

' + info: '

The number of failed attempts.

'+ + '

This chart reflects the number of \'Found\' lines. '+ + 'Found means a line in the service’s log file matches the failregex in its filter.

' }, 'fail2ban.bans': { - info: '

禁令数量。

'+ - '

此图表反映了\'Ban\'和\'Restore Ban\'行的数量。 '+ - '当上次配置的间隔(查找时间)发生失败的尝试次数(最大尝试)时,就会发生禁用操作。

' + info: '

The number of bans.

'+ + '

This chart reflects the number of \'Ban\' and \'Restore Ban\' lines. '+ + 'Ban action happens when the number of failed attempts (maxretry) occurred in the last configured interval (findtime).

' }, 'fail2ban.banned_ips': { - info: '

禁用IP地址的数量。

' + info: '

The number of banned IP addresses.

' }, }; diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/index.html b/luci-app-netdata/root/usr/share/netdata/webcn/index.html index 6bef614de..5a81a58ee 100644 --- a/luci-app-netdata/root/usr/share/netdata/webcn/index.html +++ b/luci-app-netdata/root/usr/share/netdata/webcn/index.html @@ -12,5 +12,5 @@ theme = localStorage.getItem('netdataTheme') || 'slate'; } const overlayEl = document.getElementById('loadOverlay'); - overlayEl.innerHTML = 'netdata
即时效能监控,做正确的事!
'; - overlayEl.style = theme == 'slate' ? "background-color: #272b30; color: #373b40;" : "background-color: #fff; color: #ddd;";
\ No newline at end of file + overlayEl.innerHTML = 'netdata
实时性能监控,已经安装,未启动!
'; + overlayEl.style = theme == 'slate' ? "background-color: #272b30; color: #373b40;" : "background-color: #fff; color: #ddd;";
\ No newline at end of file diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/main.js b/luci-app-netdata/root/usr/share/netdata/webcn/main.js index 8ecc86995..8c00600de 100644 --- a/luci-app-netdata/root/usr/share/netdata/webcn/main.js +++ b/luci-app-netdata/root/usr/share/netdata/webcn/main.js @@ -1,5 +1,5 @@ // Main JavaScript file for the Netdata GUI. -// 翻译 By Jason + // Codacy declarations /* global NETDATA */ @@ -1511,7 +1511,7 @@ function headMain(os, charts, duration) { if (typeof charts['system.io'] !== 'undefined') { head += '
警报.'; if (!isMemoryModeDbEngine) { - sidebar += '
 
获取更多历史记录 ' + - '配置Netdata\'s 历史 或使用 DB 引擎.'; + sidebar += '
 
获取更多历史记录 '; } sidebar += '
 
netdata
' + data.version.toString() + ''; - sidebar += ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -2039,7 +2037,7 @@ function clipboardCopyBadgeEmbed(url) { function alarmsUpdateModal() { var active = '

触发警报

'; var all = '

所有作用中的警报

'; - var footer = '
netdata badges 会自动重新整理。不同颜色分表代表的警报状态:  红色  表示重大,  橘色  表示警告,  绿色  表示良好,  灰色  表示未定义 (例如无资料或无状态),  黑色  表示尚未初始化。您可以复制这里的网址并将它们嵌入到任一个网页。
netdata 能够发送这些警报通知。请参阅 这个设定档 了解更多资讯。'; + var footer = '
netdata badges 会自动重新整理。不同颜色分表代表的警报状态: 红色  表示重大, 橘色  表示警告, 绿色  表示良好, 灰色  表示未定义 (例如无资料或无状态), 黑色  表示尚未初始化。您可以复制这里的网址并将它们嵌入到任一个网页。
netdata 能够发送这些警报通知。请参阅 这个设定档 了解更多资讯。'; loadClipboard(function () { }); @@ -2316,7 +2314,7 @@ function alarmsUpdateModal() { all += "
"; if (!count_active) { - active += '

一切正常。没有警报。
'; + active += '

Everything is normal. No raised alarms.
'; } else { active += footer; } @@ -2575,7 +2573,7 @@ function alarmsUpdateModal() { }, { field: 'duration', - title: 'Last Duration', + title: '上次持续时间', titleTooltip: 'The duration the alarm was at its previous state, just before this event', formatter: function (value, row, index) { void (row); @@ -2589,7 +2587,7 @@ function alarmsUpdateModal() { }, { field: 'non_clear_duration', - title: 'Raised Duration', + title: '提升的持续时间', titleTooltip: 'The duration the alarm was raised, just before this event', formatter: function (value, row, index) { void (row); @@ -2603,7 +2601,7 @@ function alarmsUpdateModal() { }, { field: 'recipient', - title: 'Recipient', + title: '收件人', titleTooltip: 'The recipient of this event', align: 'center', valign: 'middle', @@ -2612,7 +2610,7 @@ function alarmsUpdateModal() { }, { field: 'processed', - title: 'Processed Status', + title: '已处理状态', titleTooltip: 'True when this event is processed', formatter: function (value, row, index) { void (row); @@ -2631,7 +2629,7 @@ function alarmsUpdateModal() { }, { field: 'updated', - title: 'Updated Status', + title: '更新的状态', titleTooltip: 'True when this event has been updated by another event', formatter: function (value, row, index) { void (row); @@ -2650,7 +2648,7 @@ function alarmsUpdateModal() { }, { field: 'updated_by_id', - title: 'Updated By ID', + title: '由ID更新', titleTooltip: 'The unique ID of the event that obsoleted this one', formatter: function (value, row, index) { void (row); @@ -2664,7 +2662,7 @@ function alarmsUpdateModal() { }, { field: 'updates_id', - title: 'Updates ID', + title: '更新ID', titleTooltip: 'The unique ID of the event obsoleted because of this event', formatter: function (value, row, index) { void (row); @@ -2678,7 +2676,7 @@ function alarmsUpdateModal() { }, { field: 'exec', - title: 'Script', + title: '脚本', titleTooltip: 'The script to handle the event notification', align: 'center', valign: 'middle', @@ -2687,7 +2685,7 @@ function alarmsUpdateModal() { }, { field: 'exec_run', - title: 'Script Run At', + title: '脚本运行时间', titleTooltip: 'The date and time the script has been ran', formatter: function (value, row, index) { void (row); @@ -2701,7 +2699,7 @@ function alarmsUpdateModal() { }, { field: 'exec_code', - title: 'Script Return Value', + title: '脚本返回值', titleTooltip: 'The return code of the script', formatter: function (value, row, index) { void (row); @@ -2720,7 +2718,7 @@ function alarmsUpdateModal() { }, { field: 'delay', - title: 'Script Delay', + title: '脚本延迟', titleTooltip: 'The hysteresis of the notification', formatter: function (value, row, index) { void (row); @@ -2735,7 +2733,7 @@ function alarmsUpdateModal() { }, { field: 'delay_up_to_timestamp', - title: 'Script Delay Run At', + title: '脚本延迟运行时间', titleTooltip: 'The date and time the script should be run, after hysteresis', formatter: function (value, row, index) { void (row); @@ -2749,7 +2747,7 @@ function alarmsUpdateModal() { }, { field: 'info', - title: '说明', + title: '描述', titleTooltip: 'A short description of the alarm', align: 'center', valign: 'middle', @@ -2758,7 +2756,7 @@ function alarmsUpdateModal() { }, { field: 'source', - title: '警报来源', + title: '报警源', titleTooltip: 'The source of configuration of the alarm', align: 'center', valign: 'middle', @@ -2974,7 +2972,7 @@ function getGithubLatestVersion(callback) { callback(data); }) .fail(function () { - versionLog('从 github 下载最新版本 ID 失败!'); + versionLog('从 github 下载最新版本 ID 失败。'); callback(null); }); } @@ -3047,19 +3045,10 @@ function notifyForUpdate(force) { if (sha1 === null) { save = false; - versionLog('

取得您的 netdata 版本失败!

You can always get the latest netdata from its github page.

'); - } else if (sha2 === null) { - save = false; - versionLog('

从 github 取得 netdata 最新版本失败。

您也可以从 its github page 取得最新 netdata 版本。

'); - } else if (versionsMatch(sha1, sha2)) { + versionLog('

取得您的 netdata 版本失败!

'); + else { save = true; - versionLog('

您已经是最新版本的 netdata!

还没有更新?
或许,我们还需要一些动力继续前进!

如果您还没有做好更新的准备,请您 到 github 给 netdata at its github page.

'); - } else { - save = true; - var compare = 'https://learn.netdata.cloud/docs/agent/changelog/'; - versionLog('

New version of netdata available!

Latest version: ' + sha2 + '

Click here for the changes log and
click here for directions on updating your netdata installation.

We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
Keeping your netdata updated is generally a good idea.

'); - - document.getElementById('update_badge').innerHTML = '!'; + versionLog('

您已经是最新版本的 netdata!

'); } if (save) { @@ -4264,7 +4253,6 @@ function runOnceOnDashboardWithjQuery() { versionLog('checking, please wait...'); }) .on('shown.bs.modal', function () { - notifyForUpdate(true); }); // ------------------------------------------------------------------------ @@ -4484,7 +4472,6 @@ function finalizePage() { ga('send', 'pageview', '/demosite/' + window.location.host); }, 2000); } else { - notifyForUpdate(); } if (urlOptions.show_alarms === true) { diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/netdata b/luci-app-netdata/root/usr/share/netdata/webcn/netdata new file mode 100644 index 000000000..83f9eda99 --- /dev/null +++ b/luci-app-netdata/root/usr/share/netdata/webcn/netdata @@ -0,0 +1,6 @@ + +config netdata 'netdata' + option logger '1' + option enabled '0' + option port '19990' + diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf b/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf new file mode 100644 index 000000000..9b2c9d24b --- /dev/null +++ b/luci-app-netdata/root/usr/share/netdata/webcn/netdata.conf @@ -0,0 +1,52 @@ +# Full configuration can be retrieved from the running +# server at http://localhost:19999/netdata.conf +# +# Example: +# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf +# + +[global] + update every = 2 + memory deduplication (ksm) = no + debug log = syslog + error log = syslog + access log = none + run as user = root + +[web] + allow connections from = * + allow dashboard from = * + +[plugins] + cgroups = no + apps = no + charts.d = no + fping = no + node.d = no + python.d = no + +[plugin:proc] + ipc =no + /proc/sysvipc/shm = no + /sys/devices/system/edac/mc = no + /sys/devices/system/node = no + /proc/net/sockstat = no + /proc/net/netstat = no + /proc/net/snmp = no + /proc/net/softnet_stat = no + /proc/net/sctp/snmp = no + /proc/net/ip_vs/stats = no + /proc/net/stat/synproxy = no + /sys/kernel/mm/ksm = no + /dev/mapper = no + semaphore totals = no + +[plugin:proc:/proc/diskstats] + path to /dev/vx/dsk = + path to /dev/disk/by-label = + +[health] + enabled = no + +[plugin:proc:ipc] + shared memory totals = no diff --git a/luci-app-netdata/root/usr/share/netdata/webcn/netdata.init b/luci-app-netdata/root/usr/share/netdata/webcn/netdata.init new file mode 100755 index 000000000..9e088294e --- /dev/null +++ b/luci-app-netdata/root/usr/share/netdata/webcn/netdata.init @@ -0,0 +1,46 @@ +#!/bin/sh /etc/rc.common + +START=99 +USE_PROCD=1 + +APPBINARY=/usr/sbin/netdata +CONFIGFILE=/etc/netdata/netdata.conf + +get_config() { + config_get_bool enabled $1 enabled 1 + config_get_bool logger $1 logger 1 + config_get port $1 port 19999 +} + +netdata_prepare() { + pgrep -f /usr/sbin/netdata | xargs kill -9 >/dev/null 2>&1 + logger -t netdata -p warn "netdata is stop." +} + +stop_service() { + netdata_prepare +} + +start_service() { + netdata_prepare + config_load netdata + config_foreach get_config netdata + [ x$enabled == x1 ] || return 1 + mkdir -m 0755 -p /var/cache/netdata + logger -t netdata -p warn "netdata is start." + chown nobody /var/cache/netdata + mkdir -m 0755 -p /var/lib/netdata + chown nobody /var/lib/netdata + mkdir -m 0755 -p /var/log/netdata + chown nobody /var/log/netdata + procd_open_instance + procd_set_param command $APPBINARY -D -c $CONFIGFILE -p $port:$port + [ "x$logger" == x1 ] && procd_set_param stderr 1 + procd_set_param file $CONFIGFILE + procd_set_param respawn + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger "netdata" +} diff --git a/luci-app-netdata/web/dashboard.js b/luci-app-netdata/web/dashboard.js deleted file mode 100644 index 9642d3ac0..000000000 --- a/luci-app-netdata/web/dashboard.js +++ /dev/null @@ -1,10377 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// 时间 By Jason -// DO NOT EDIT: This file is automatically generated from the source files in src/ - -// ---------------------------------------------------------------------------- -// You can set the following variables before loading this script: - -// 'use strict'; - -/*global netdataNoDygraphs *//* boolean, disable dygraph charts - * (default: false) */ -/*global netdataNoSparklines *//* boolean, disable sparkline charts - * (default: false) */ -/*global netdataNoPeitys *//* boolean, disable peity charts - * (default: false) */ -/*global netdataNoGoogleCharts *//* boolean, disable google charts - * (default: false) */ -/*global netdataNoMorris *//* boolean, disable morris charts - * (default: false) */ -/*global netdataNoEasyPieChart *//* boolean, disable easypiechart charts - * (default: false) */ -/*global netdataNoGauge *//* boolean, disable gauge.js charts - * (default: false) */ -/*global netdataNoD3 *//* boolean, disable d3 charts - * (default: false) */ -/*global netdataNoC3 *//* boolean, disable c3 charts - * (default: false) */ -/*global netdataNoD3pie *//* boolean, disable d3pie charts - * (default: false) */ -/*global netdataNoBootstrap *//* boolean, disable bootstrap - disables help too - * (default: false) */ -/*global netdataNoFontAwesome *//* boolean, disable fontawesome (do not load it) - * (default: false) */ -/*global netdataIcons *//* object, overwrite netdata fontawesome icons - * (default: null) */ -/*global netdataDontStart *//* boolean, do not start the thread to process the charts - * (default: false) */ -/*global netdataErrorCallback *//* function, callback to be called when the dashboard encounters an error - * (default: null) */ -/*global netdataRegistry:true *//* boolean, use the netdata registry - * (default: false) */ -/*global netdataNoRegistry *//* boolean, included only for compatibility with existing custom dashboard - * (obsolete - do not use this any more) */ -/*global netdataRegistryCallback *//* function, callback that will be invoked with one param: the URLs from the registry - * (default: null) */ -/*global netdataShowHelp:true *//* boolean, disable charts help - * (default: true) */ -/*global netdataShowAlarms:true *//* boolean, enable alarms checks and notifications - * (default: false) */ -/*global netdataRegistryAfterMs:true *//* ms, delay registry use at started - * (default: 1500) */ -/*global netdataCallback *//* function, callback to be called when netdata is ready to start - * (default: null) - * netdata will be running while this is called - * (call NETDATA.pause to stop it) */ -/*global netdataPrepCallback *//* function, callback to be called before netdata does anything else - * (default: null) */ -/*global netdataServer *//* string, the URL of the netdata server to use - * (default: the URL the page is hosted at) */ -/*global netdataServerStatic *//* string, the URL of the netdata server to use for static files - * (default: netdataServer) */ -/*global netdataSnapshotData *//* object, a netdata snapshot loaded - * (default: null) */ -/*global netdataAlarmsRecipients *//* array, an array of alarm recipients to show notifications for - * (default: null) */ -/*global netdataAlarmsRemember *//* boolean, keep our position in the alarm log at browser local storage - * (default: true) */ -/*global netdataAlarmsActiveCallback *//* function, a hook for the alarm logs - * (default: undefined) */ -/*global netdataAlarmsNotifCallback *//* function, a hook for alarm notifications - * (default: undefined) */ -/*global netdataIntersectionObserver *//* boolean, enable or disable the use of intersection observer - * (default: true) */ -/*global netdataCheckXSS *//* boolean, enable or disable checking for XSS issues - * (default: false) */ - -// ---------------------------------------------------------------------------- -// global namespace - -// Should stay var! -var NETDATA = window.NETDATA || {}; - -(function(window, document, $, undefined) { - -// *** src/dashboard.js/utils.js - -NETDATA.name2id = function (s) { - return s - .replace(/ /g, '_') - .replace(/:/g, '_') - .replace(/\(/g, '_') - .replace(/\)/g, '_') - .replace(/\./g, '_') - .replace(/\//g, '_'); -}; - -NETDATA.encodeURIComponent = function (s) { - if (typeof(s) === 'string') { - return encodeURIComponent(s); - } - - return s; -}; - -/// A heuristic for detecting slow devices. -let isSlowDeviceResult = undefined; -const isSlowDevice = function () { - if (!isSlowDeviceResult) { - return isSlowDeviceResult; - } - - try { - let ua = navigator.userAgent.toLowerCase(); - - let iOS = /ipad|iphone|ipod/.test(ua) && !window.MSStream; - let android = /android/.test(ua) && !window.MSStream; - isSlowDeviceResult = (iOS || android); - } catch (e) { - isSlowDeviceResult = false; - } - - return isSlowDeviceResult; -}; - -NETDATA.guid = function () { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -}; - -NETDATA.zeropad = function (x) { - if (x > -10 && x < 10) { - return '0' + x.toString(); - } else { - return x.toString(); - } -}; - -NETDATA.seconds4human = function (seconds, options) { - let defaultOptions = { - now: '现在', - space: ' ', - negative_suffix: '前', - day: '日', - days: '日', - hour: '小时', - hours: '小时', - minute: '分钟', - minutes: '分钟', - second: '秒', - seconds: '秒', - and: '及' - }; - - if (typeof options !== 'object') { - options = defaultOptions; - } else { - for (var x in defaultOptions) { - if (typeof options[x] !== 'string') { - options[x] = defaultOptions[x]; - } - } - } - - if (typeof seconds === 'string') { - seconds = parseInt(seconds, 10); - } - - if (seconds === 0) { - return options.now; - } - - let suffix = ''; - if (seconds < 0) { - seconds = -seconds; - if (options.negative_suffix !== '') { - suffix = options.space + options.negative_suffix; - } - } - - let days = Math.floor(seconds / 86400); - seconds -= (days * 86400); - - let hours = Math.floor(seconds / 3600); - seconds -= (hours * 3600); - - let minutes = Math.floor(seconds / 60); - seconds -= (minutes * 60); - - let strings = []; - - if (days > 1) { - strings.push(days.toString() + options.space + options.days); - } else if (days === 1) { - strings.push(days.toString() + options.space + options.day); - } - - if (hours > 1) { - strings.push(hours.toString() + options.space + options.hours); - } else if (hours === 1) { - strings.push(hours.toString() + options.space + options.hour); - } - - if (minutes > 1) { - strings.push(minutes.toString() + options.space + options.minutes); - } else if (minutes === 1) { - strings.push(minutes.toString() + options.space + options.minute); - } - - if (seconds > 1) { - strings.push(Math.floor(seconds).toString() + options.space + options.seconds); - } else if (seconds === 1) { - strings.push(Math.floor(seconds).toString() + options.space + options.second); - } - - if (strings.length === 1) { - return strings.pop() + suffix; - } - - let last = strings.pop(); - return strings.join(", ") + " " + options.and + " " + last + suffix; -}; - -// ---------------------------------------------------------------------------------------------------------------- -// element data attributes - -NETDATA.dataAttribute = function (element, attribute, def) { - let key = 'data-' + attribute.toString(); - if (element.hasAttribute(key)) { - let data = element.getAttribute(key); - - if (data === 'true') { - return true; - } - if (data === 'false') { - return false; - } - if (data === 'null') { - return null; - } - - // Only convert to a number if it doesn't change the string - if (data === +data + '') { - return +data; - } - - if (/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/.test(data)) { - return JSON.parse(data); - } - - return data; - } else { - return def; - } -}; - -NETDATA.dataAttributeBoolean = function (element, attribute, def) { - let value = NETDATA.dataAttribute(element, attribute, def); - - if (value === true || value === false) // gmosx: Love this :) - { - return value; - } - - if (typeof(value) === 'string') { - if (value === 'yes' || value === 'on') { - return true; - } - - if (value === '' || value === 'no' || value === 'off' || value === 'null') { - return false; - } - - return def; - } - - if (typeof(value) === 'number') { - return value !== 0; - } - - return def; -}; - -// ---------------------------------------------------------------------------------------------------------------- -// fast numbers formatting - -NETDATA.fastNumberFormat = { - formattersFixed: [], - formattersZeroBased: [], - - // this is the fastest and the preferred - getIntlNumberFormat: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - - return this.formattersZeroBased[key]; - } else { - // this is never used - // it is added just for completeness - return new Intl.NumberFormat(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }, - - // this respects locale - getLocaleString: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - - return this.formattersZeroBased[key]; - } else { - return { - format: function (value) { - return value.toLocaleString(undefined, { - // style: 'decimal', - // minimumIntegerDigits: 1, - // minimumSignificantDigits: 1, - // maximumSignificantDigits: 1, - useGrouping: true, - minimumFractionDigits: min, - maximumFractionDigits: max - }); - } - }; - } - }, - - // the fallback - getFixed: function (min, max) { - let key = max; - if (min === max) { - if (typeof this.formattersFixed[key] === 'undefined') { - this.formattersFixed[key] = { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - - return this.formattersFixed[key]; - } else if (min === 0) { - if (typeof this.formattersZeroBased[key] === 'undefined') { - this.formattersZeroBased[key] = { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - - return this.formattersZeroBased[key]; - } else { - return { - format: function (value) { - if (value === 0) { - return "0"; - } - return value.toFixed(max); - } - }; - } - }, - - testIntlNumberFormat: function () { - let value = 1.12345; - let e1 = "1.12", e2 = "1,12"; - let s = ""; - - try { - let x = new Intl.NumberFormat(undefined, { - useGrouping: true, - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }); - - s = x.format(value); - } catch (e) { - s = ""; - } - - // console.log('NumberFormat: ', s); - return (s === e1 || s === e2); - }, - - testLocaleString: function () { - let value = 1.12345; - let e1 = "1.12", e2 = "1,12"; - let s = ""; - - try { - s = value.toLocaleString(undefined, { - useGrouping: true, - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }); - } catch (e) { - s = ""; - } - - // console.log('localeString: ', s); - return (s === e1 || s === e2); - }, - - // on first run we decide which formatter to use - get: function (min, max) { - if (this.testIntlNumberFormat()) { - // console.log('numberformat'); - this.get = this.getIntlNumberFormat; - } else if (this.testLocaleString()) { - // console.log('localestring'); - this.get = this.getLocaleString; - } else { - // console.log('fixed'); - this.get = this.getFixed; - } - return this.get(min, max); - } -}; - -// ---------------------------------------------------------------------------------------------------------------- -// Detect the netdata server - -// http://stackoverflow.com/questions/984510/what-is-my-script-src-url -// http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url -NETDATA._scriptSource = function () { - let script = null; - - if (typeof document.currentScript !== 'undefined') { - script = document.currentScript; - } else { - const all_scripts = document.getElementsByTagName('script'); - script = all_scripts[all_scripts.length - 1]; - } - - if (typeof script.getAttribute.length !== 'undefined') { - script = script.src; - } else { - script = script.getAttribute('src', -1); - } - - return script; -}; - -// *** src/dashboard.js/server-detection.js - -if (typeof netdataServer !== 'undefined') { - NETDATA.serverDefault = netdataServer; -} else { - let s = NETDATA._scriptSource(); - if (s) { - NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)?$/g, ""); - } else { - console.log('WARNING: Cannot detect the URL of the netdata server.'); - NETDATA.serverDefault = null; - } -} - -if (NETDATA.serverDefault === null) { - NETDATA.serverDefault = ''; -} else if (NETDATA.serverDefault.slice(-1) !== '/') { - NETDATA.serverDefault += '/'; -} - -if (typeof netdataServerStatic !== 'undefined' && netdataServerStatic !== null && netdataServerStatic !== '') { - NETDATA.serverStatic = netdataServerStatic; - if (NETDATA.serverStatic.slice(-1) !== '/') { - NETDATA.serverStatic += '/'; - } -} else { - NETDATA.serverStatic = NETDATA.serverDefault; -} - -// *** src/dashboard.js/dependencies.js - -// default URLs for all the external files we need -// make them RELATIVE so that the whole thing can also be -// installed under a web server -NETDATA.jQuery = NETDATA.serverStatic + 'lib/jquery-3.6.0.min.js'; -NETDATA.peity_js = NETDATA.serverStatic + 'lib/jquery.peity-3.2.0.min.js'; -NETDATA.sparkline_js = NETDATA.serverStatic + 'lib/jquery.sparkline-2.1.2.min.js'; -NETDATA.easypiechart_js = NETDATA.serverStatic + 'lib/jquery.easypiechart-97b5824.min.js'; -NETDATA.gauge_js = NETDATA.serverStatic + 'lib/gauge-1.3.2.min.js'; -NETDATA.dygraph_js = NETDATA.serverStatic + 'lib/dygraph-c91c859.min.js'; -NETDATA.dygraph_smooth_js = NETDATA.serverStatic + 'lib/dygraph-smooth-plotter-c91c859.js'; -// NETDATA.raphael_js = NETDATA.serverStatic + 'lib/raphael-2.2.4-min.js'; -// NETDATA.c3_js = NETDATA.serverStatic + 'lib/c3-0.4.18.min.js'; -// NETDATA.c3_css = NETDATA.serverStatic + 'css/c3-0.4.18.min.css'; -NETDATA.d3pie_js = NETDATA.serverStatic + 'lib/d3pie-0.2.1-netdata-3.js'; -NETDATA.d3_js = NETDATA.serverStatic + 'lib/d3-4.12.2.min.js'; -// NETDATA.morris_js = NETDATA.serverStatic + 'lib/morris-0.5.1.min.js'; -// NETDATA.morris_css = NETDATA.serverStatic + 'css/morris-0.5.1.css'; -NETDATA.google_js = 'https://www.google.com/jsapi'; -// Error Handling - -NETDATA.errorCodes = { - 100: {message: "Cannot load chart library", alert: true}, - 101: {message: "Cannot load jQuery", alert: true}, - 402: {message: "Chart library not found", alert: false}, - 403: {message: "Chart library not enabled/is failed", alert: false}, - 404: {message: "Chart not found", alert: false}, - 405: {message: "Cannot download charts index from server", alert: true}, - 406: {message: "Invalid charts index downloaded from server", alert: true}, - 407: {message: "Cannot HELLO netdata server", alert: false}, - 408: {message: "Netdata servers sent invalid response to HELLO", alert: false}, - 409: {message: "Cannot ACCESS netdata registry", alert: false}, - 410: {message: "Netdata registry ACCESS failed", alert: false}, - 411: {message: "Netdata registry server send invalid response to DELETE ", alert: false}, - 412: {message: "Netdata registry DELETE failed", alert: false}, - 413: {message: "Netdata registry server send invalid response to SWITCH ", alert: false}, - 414: {message: "Netdata registry SWITCH failed", alert: false}, - 415: {message: "Netdata alarms download failed", alert: false}, - 416: {message: "Netdata alarms log download failed", alert: false}, - 417: {message: "Netdata registry server send invalid response to SEARCH ", alert: false}, - 418: {message: "Netdata registry SEARCH failed", alert: false} -}; - -NETDATA.errorLast = { - code: 0, - message: "", - datetime: 0 -}; - -NETDATA.error = function (code, msg) { - NETDATA.errorLast.code = code; - NETDATA.errorLast.message = msg; - NETDATA.errorLast.datetime = Date.now(); - - console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); - - let ret = true; - if (typeof netdataErrorCallback === 'function') { - ret = netdataErrorCallback('system', code, msg); - } - - if (ret && NETDATA.errorCodes[code].alert) { - alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg); - } -}; - -NETDATA.errorReset = function () { - NETDATA.errorLast.code = 0; - NETDATA.errorLast.message = "You are doing fine!"; - NETDATA.errorLast.datetime = 0; -}; -// *** src/dashboard.js/compatibility.js - -// Compatibility fixes. - -// fix IE issue with console -if (!window.console) { - window.console = { - log: function () { - } - }; -} - -// if string.endsWith is not defined, define it -if (typeof String.prototype.endsWith !== 'function') { - String.prototype.endsWith = function (s) { - if (s.length > this.length) { - return false; - } - return this.slice(-s.length) === s; - }; -} - -// if string.startsWith is not defined, define it -if (typeof String.prototype.startsWith !== 'function') { - String.prototype.startsWith = function (s) { - if (s.length > this.length) { - return false; - } - return this.slice(s.length) === s; - }; -} -// ---------------------------------------------------------------------------------------------------------------- -// XSS checks - -NETDATA.xss = { - enabled: (typeof netdataCheckXSS === 'undefined') ? false : netdataCheckXSS, - enabled_for_data: (typeof netdataCheckXSS === 'undefined') ? false : netdataCheckXSS, - - string: function (s) { - return s.toString() - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - }, - - object: function (name, obj, ignore_regex) { - if (typeof ignore_regex !== 'undefined' && ignore_regex.test(name)) { - // console.log('XSS: ignoring "' + name + '"'); - return obj; - } - - switch (typeof(obj)) { - case 'string': - const ret = this.string(obj); - if (ret !== obj) { - console.log('XSS protection changed string ' + name + ' from "' + obj + '" to "' + ret + '"'); - } - return ret; - - case 'object': - if (obj === null) { - return obj; - } - - if (Array.isArray(obj)) { - // console.log('checking array "' + name + '"'); - - let len = obj.length; - while (len--) { - obj[len] = this.object(name + '[' + len + ']', obj[len], ignore_regex); - } - } else { - // console.log('checking object "' + name + '"'); - - for (var i in obj) { - if (obj.hasOwnProperty(i) === false) { - continue; - } - if (this.string(i) !== i) { - console.log('XSS protection removed invalid object member "' + name + '.' + i + '"'); - delete obj[i]; - } else { - obj[i] = this.object(name + '.' + i, obj[i], ignore_regex); - } - } - } - return obj; - - default: - return obj; - } - }, - - checkOptional: function (name, obj, ignore_regex) { - if (this.enabled) { - //console.log('XSS: checking optional "' + name + '"...'); - return this.object(name, obj, ignore_regex); - } - return obj; - }, - - checkAlways: function (name, obj, ignore_regex) { - //console.log('XSS: checking always "' + name + '"...'); - return this.object(name, obj, ignore_regex); - }, - - checkData: function (name, obj, ignore_regex) { - if (this.enabled_for_data) { - //console.log('XSS: checking data "' + name + '"...'); - return this.object(name, obj, ignore_regex); - } - return obj; - } -}; -NETDATA.colorHex2Rgb = function (hex) { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace(shorthandRegex, function (m, r, g, b) { - return r + r + g + g + b + b; - }); - - let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; -}; - -NETDATA.colorLuminance = function (hex, lum) { - // validate hex string - hex = String(hex).replace(/[^0-9a-f]/gi, ''); - if (hex.length < 6) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - - lum = lum || 0; - - // convert to decimal and change luminosity - let rgb = "#"; - for (let i = 0; i < 3; i++) { - let c = parseInt(hex.substr(i * 2, 2), 16); - c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); - rgb += ("00" + c).substr(c.length); - } - - return rgb; -}; -NETDATA.unitsConversion = { - keys: {}, // keys for data-common-units - latest: {}, // latest selected units for data-common-units - - globalReset: function () { - this.keys = {}; - this.latest = {}; - }, - - scalableUnits: { - 'packets/s': { - 'pps': 1, - 'Kpps': 1000, - 'Mpps': 1000000 - }, - 'pps': { - 'pps': 1, - 'Kpps': 1000, - 'Mpps': 1000000 - }, - 'kilobits/s': { - 'bits/s': 1 / 1000, - 'kilobits/s': 1, - 'megabits/s': 1000, - 'gigabits/s': 1000000, - 'terabits/s': 1000000000 - }, - 'bytes/s': { - 'bytes/s': 1, - 'kilobytes/s': 1024, - 'megabytes/s': 1024 * 1024, - 'gigabytes/s': 1024 * 1024 * 1024, - 'terabytes/s': 1024 * 1024 * 1024 * 1024 - }, - 'kilobytes/s': { - 'bytes/s': 1 / 1024, - 'kilobytes/s': 1, - 'megabytes/s': 1024, - 'gigabytes/s': 1024 * 1024, - 'terabytes/s': 1024 * 1024 * 1024 - }, - 'B/s': { - 'B/s': 1, - 'KiB/s': 1024, - 'MiB/s': 1024 * 1024, - 'GiB/s': 1024 * 1024 * 1024, - 'TiB/s': 1024 * 1024 * 1024 * 1024 - }, - 'KB/s': { - 'B/s': 1 / 1024, - 'KB/s': 1, - 'MB/s': 1024, - 'GB/s': 1024 * 1024, - 'TB/s': 1024 * 1024 * 1024 - }, - 'KiB/s': { - 'B/s': 1 / 1024, - 'KiB/s': 1, - 'MiB/s': 1024, - 'GiB/s': 1024 * 1024, - 'TiB/s': 1024 * 1024 * 1024 - }, - 'B': { - 'B': 1, - 'KiB': 1024, - 'MiB': 1024 * 1024, - 'GiB': 1024 * 1024 * 1024, - 'TiB': 1024 * 1024 * 1024 * 1024, - 'PiB': 1024 * 1024 * 1024 * 1024 * 1024 - }, - 'KB': { - 'B': 1 / 1024, - 'KB': 1, - 'MB': 1024, - 'GB': 1024 * 1024, - 'TB': 1024 * 1024 * 1024 - }, - 'KiB': { - 'B': 1 / 1024, - 'KiB': 1, - 'MiB': 1024, - 'GiB': 1024 * 1024, - 'TiB': 1024 * 1024 * 1024 - }, - 'MB': { - 'B': 1 / (1024 * 1024), - 'KB': 1 / 1024, - 'MB': 1, - 'GB': 1024, - 'TB': 1024 * 1024, - 'PB': 1024 * 1024 * 1024 - }, - 'MiB': { - 'B': 1 / (1024 * 1024), - 'KiB': 1 / 1024, - 'MiB': 1, - 'GiB': 1024, - 'TiB': 1024 * 1024, - 'PiB': 1024 * 1024 * 1024 - }, - 'GB': { - 'B': 1 / (1024 * 1024 * 1024), - 'KB': 1 / (1024 * 1024), - 'MB': 1 / 1024, - 'GB': 1, - 'TB': 1024, - 'PB': 1024 * 1024, - 'EB': 1024 * 1024 * 1024 - }, - 'GiB': { - 'B': 1 / (1024 * 1024 * 1024), - 'KiB': 1 / (1024 * 1024), - 'MiB': 1 / 1024, - 'GiB': 1, - 'TiB': 1024, - 'PiB': 1024 * 1024, - 'EiB': 1024 * 1024 * 1024 - }, - 'num': { - 'num': 1, - 'num (K)': 1000, - 'num (M)': 1000000, - 'num (G)': 1000000000, - 'num (T)': 1000000000000 - } - /* - 'milliseconds': { - 'seconds': 1000 - }, - 'seconds': { - 'milliseconds': 0.001, - 'seconds': 1, - 'minutes': 60, - 'hours': 3600, - 'days': 86400 - } - */ - }, - - convertibleUnits: { - 'Celsius': { - 'Fahrenheit': { - check: function (max) { - void(max); - return NETDATA.options.current.temperature === 'fahrenheit'; - }, - convert: function (value) { - return value * 9 / 5 + 32; - } - } - }, - 'celsius': { - 'fahrenheit': { - check: function (max) { - void(max); - return NETDATA.options.current.temperature === 'fahrenheit'; - }, - convert: function (value) { - return value * 9 / 5 + 32; - } - } - }, - 'seconds': { - 'time': { - check: function (max) { - void(max); - return NETDATA.options.current.seconds_as_time; - }, - convert: function (seconds) { - return NETDATA.unitsConversion.seconds2time(seconds); - } - } - }, - 'milliseconds': { - 'milliseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max < 1000; - }, - convert: function (milliseconds) { - let tms = Math.round(milliseconds * 10); - milliseconds = Math.floor(tms / 10); - - tms -= milliseconds * 10; - - return (milliseconds).toString() + '.' + tms.toString(); - } - }, - 'seconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max >= 1000 && max < 60000; - }, - convert: function (milliseconds) { - milliseconds = Math.round(milliseconds); - - let seconds = Math.floor(milliseconds / 1000); - milliseconds -= seconds * 1000; - - milliseconds = Math.round(milliseconds / 10); - - return seconds.toString() + '.' - + NETDATA.zeropad(milliseconds); - } - }, - 'M:SS.ms': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max >= 60000; - }, - convert: function (milliseconds) { - milliseconds = Math.round(milliseconds); - - let minutes = Math.floor(milliseconds / 60000); - milliseconds -= minutes * 60000; - - let seconds = Math.floor(milliseconds / 1000); - milliseconds -= seconds * 1000; - - milliseconds = Math.round(milliseconds / 10); - - return minutes.toString() + ':' - + NETDATA.zeropad(seconds) + '.' - + NETDATA.zeropad(milliseconds); - } - } - }, - 'nanoseconds': { - 'nanoseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time && max < 1000; - }, - convert: function (nanoseconds) { - let tms = Math.round(nanoseconds * 10); - nanoseconds = Math.floor(tms / 10); - - tms -= nanoseconds * 10; - - return (nanoseconds).toString() + '.' + tms.toString(); - } - }, - 'microseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 && max < 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let microseconds = Math.floor(nanoseconds / 1000); - nanoseconds -= microseconds * 1000; - - nanoseconds = Math.round(nanoseconds / 10 ); - - return microseconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - 'milliseconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 * 1000 && max < 1000 * 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let milliseconds = Math.floor(nanoseconds / 1000 / 1000); - nanoseconds -= milliseconds * 1000 * 1000; - - nanoseconds = Math.round(nanoseconds / 1000 / 10); - - return milliseconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - 'seconds': { - check: function (max) { - return NETDATA.options.current.seconds_as_time - && max >= 1000 * 1000 * 1000; - }, - convert: function (nanoseconds) { - nanoseconds = Math.round(nanoseconds); - - let seconds = Math.floor(nanoseconds / 1000 / 1000 / 1000); - nanoseconds -= seconds * 1000 * 1000 * 1000; - - nanoseconds = Math.round(nanoseconds / 1000 / 1000 / 10); - - return seconds.toString() + '.' - + NETDATA.zeropad(nanoseconds); - } - }, - } - }, - - seconds2time: function (seconds) { - seconds = Math.abs(seconds); - - let days = Math.floor(seconds / 86400); - seconds -= days * 86400; - - let hours = Math.floor(seconds / 3600); - seconds -= hours * 3600; - - let minutes = Math.floor(seconds / 60); - seconds -= minutes * 60; - - seconds = Math.round(seconds); - - let ms_txt = ''; - /* - let ms = seconds - Math.floor(seconds); - seconds -= ms; - ms = Math.round(ms * 1000); - - if (ms > 1) { - if (ms < 10) - ms_txt = '.00' + ms.toString(); - else if (ms < 100) - ms_txt = '.0' + ms.toString(); - else - ms_txt = '.' + ms.toString(); - } - */ - - return ((days > 0) ? days.toString() + 'd:' : '').toString() - + NETDATA.zeropad(hours) + ':' - + NETDATA.zeropad(minutes) + ':' - + NETDATA.zeropad(seconds) - + ms_txt; - }, - - // get a function that converts the units - // + every time units are switched call the callback - get: function (uuid, min, max, units, desired_units, common_units_name, switch_units_callback) { - // validate the parameters - if (typeof units === 'undefined') { - units = 'undefined'; - } - - // check if we support units conversion - if (typeof this.scalableUnits[units] === 'undefined' && typeof this.convertibleUnits[units] === 'undefined') { - // we can't convert these units - //console.log('DEBUG: ' + uuid.toString() + ' can\'t convert units: ' + units.toString()); - return function (value) { - return value; - }; - } - - // check if the caller wants the original units - if (typeof desired_units === 'undefined' || desired_units === null || desired_units === 'original' || desired_units === units) { - //console.log('DEBUG: ' + uuid.toString() + ' original units wanted'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - - // now we know we can convert the units - // and the caller wants some kind of conversion - - let tunits = null; - let tdivider = 0; - - if (typeof this.scalableUnits[units] !== 'undefined') { - // units that can be scaled - // we decide a divider - - // console.log('NETDATA.unitsConversion.get(' + units.toString() + ', ' + desired_units.toString() + ', function()) decide divider with min = ' + min.toString() + ', max = ' + max.toString()); - - if (desired_units === 'auto') { - // the caller wants to auto-scale the units - - // find the absolute maximum value that is rendered on the chart - // based on this we decide the scale - min = Math.abs(min); - max = Math.abs(max); - if (min > max) { - max = min; - } - - // find the smallest scale that provides integers - // for (x in this.scalableUnits[units]) { - // if (this.scalableUnits[units].hasOwnProperty(x)) { - // let m = this.scalableUnits[units][x]; - // if (m <= max && m > tdivider) { - // tunits = x; - // tdivider = m; - // } - // } - // } - const sunit = this.scalableUnits[units]; - for (var x of Object.keys(sunit)) { - let m = sunit[x]; - if (m <= max && m > tdivider) { - tunits = x; - tdivider = m; - } - } - - if (tunits === null || tdivider <= 0) { - // we couldn't find one - //console.log('DEBUG: ' + uuid.toString() + ' cannot find an auto-scaling candidate for units: ' + units.toString() + ' (max: ' + max.toString() + ')'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - - if (typeof common_units_name === 'string' && typeof uuid === 'string') { - // the caller wants several charts to have the same units - // data-common-units - - let common_units_key = common_units_name + '-' + units; - - // add our divider into the list of keys - let t = this.keys[common_units_key]; - if (typeof t === 'undefined') { - this.keys[common_units_key] = {}; - t = this.keys[common_units_key]; - } - t[uuid] = { - units: tunits, - divider: tdivider - }; - - // find the max divider of all charts - let common_units = t[uuid]; - for (var x in t) { - if (t.hasOwnProperty(x) && t[x].divider > common_units.divider) { - common_units = t[x]; - } - } - - // save our common_max to the latest keys - let latest = this.latest[common_units_key]; - if (typeof latest === 'undefined') { - this.latest[common_units_key] = {}; - latest = this.latest[common_units_key]; - } - latest.units = common_units.units; - latest.divider = common_units.divider; - - tunits = latest.units; - tdivider = latest.divider; - - //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', common-units=' + common_units_name.toString() + ((t[uuid].divider !== tdivider)?' USED COMMON, mine was ' + t[uuid].units:' set common').toString()); - - // apply it to this chart - switch_units_callback(tunits); - return function (value) { - if (tdivider !== latest.divider) { - // another chart switched our common units - // we should switch them too - //console.log('DEBUG: ' + uuid + ' switching units due to a common-units change, from ' + tunits.toString() + ' to ' + latest.units.toString()); - tunits = latest.units; - tdivider = latest.divider; - switch_units_callback(tunits); - } - - return value / tdivider; - }; - } else { - // the caller did not give data-common-units - // this chart auto-scales independently of all others - //console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + tunits.toString() + ' with divider ' + tdivider.toString() + ', autonomously'); - - switch_units_callback(tunits); - return function (value) { - return value / tdivider; - }; - } - } else { - // the caller wants specific units - - if (typeof this.scalableUnits[units][desired_units] !== 'undefined') { - // all good, set the new units - tdivider = this.scalableUnits[units][desired_units]; - // console.log('DEBUG: ' + uuid.toString() + ' converted units: ' + units.toString() + ' to units: ' + desired_units.toString() + ' with divider ' + tdivider.toString() + ', by reference'); - switch_units_callback(desired_units); - return function (value) { - return value / tdivider; - }; - } else { - // oops! switch back to original units - console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } - } else if (typeof this.convertibleUnits[units] !== 'undefined') { - // units that can be converted - if (desired_units === 'auto') { - for (var x in this.convertibleUnits[units]) { - if (this.convertibleUnits[units].hasOwnProperty(x)) { - if (this.convertibleUnits[units][x].check(max)) { - //console.log('DEBUG: ' + uuid.toString() + ' converting ' + units.toString() + ' to: ' + x.toString()); - switch_units_callback(x); - return this.convertibleUnits[units][x].convert; - } - } - } - - // none checked ok - //console.log('DEBUG: ' + uuid.toString() + ' no conversion available for ' + units.toString() + ' to: ' + desired_units.toString()); - switch_units_callback(units); - return function (value) { - return value; - }; - } else if (typeof this.convertibleUnits[units][desired_units] !== 'undefined') { - switch_units_callback(desired_units); - return this.convertibleUnits[units][desired_units].convert; - } else { - console.log('Units conversion from ' + units.toString() + ' to ' + desired_units.toString() + ' is not supported.'); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } else { - // hm... did we forget to implement the new type? - console.log(`Unmatched unit conversion method for units ${units.toString()}`); - switch_units_callback(units); - return function (value) { - return value; - }; - } - } -}; - -NETDATA.icons = { - left: '', - reset: '', - right: '', - zoomIn: '', - zoomOut: '', - resize: '', - lineChart: '', - areaChart: '', - noChart: '', - loading: '', - noData: '' -}; - -if (typeof netdataIcons === 'object') { - // for (let icon in NETDATA.icons) { - // if (NETDATA.icons.hasOwnProperty(icon) && typeof(netdataIcons[icon]) === 'string') - // NETDATA.icons[icon] = netdataIcons[icon]; - // } - for (var icon of Object.keys(NETDATA.icons)) { - if (typeof(netdataIcons[icon]) === 'string') { - NETDATA.icons[icon] = netdataIcons[icon] - } - } -} - -if (typeof netdataSnapshotData === 'undefined') { - netdataSnapshotData = null; -} - -if (typeof netdataShowHelp === 'undefined') { - netdataShowHelp = true; -} - -if (typeof netdataShowAlarms === 'undefined') { - netdataShowAlarms = false; -} - -if (typeof netdataRegistryAfterMs !== 'number' || netdataRegistryAfterMs < 0) { - netdataRegistryAfterMs = 0; // 1500; -} - -if (typeof netdataRegistry === 'undefined') { - // backward compatibility - netdataRegistry = (typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry === false); -} - -if (netdataRegistry === false && typeof netdataRegistryCallback === 'function') { - netdataRegistry = true; -} - -// ---------------------------------------------------------------------------------------------------------------- -// the defaults for all charts - -// if the user does not specify any of these, the following will be used - -NETDATA.chartDefaults = { - width: '100%', // the chart width - can be null - height: '100%', // the chart height - can be null - min_width: null, // the chart minimum width - can be null - library: 'dygraph', // the graphing library to use - method: 'average', // the grouping method - before: 0, // panning - after: -600, // panning - pixels_per_point: 1, // the detail of the chart - fill_luminance: 0.8 // luminance of colors in solid areas -}; - -// ---------------------------------------------------------------------------------------------------------------- -// global options - -NETDATA.options = { - pauseCallback: null, // a callback when we are really paused - - pause: false, // when enabled we don't auto-refresh the charts - - targets: [], // an array of all the state objects that are - // currently active (independently of their - // viewport visibility) - - updated_dom: true, // when true, the DOM has been updated with - // new elements we have to check. - - auto_refresher_fast_weight: 0, // this is the current time in ms, spent - // rendering charts continuously. - // used with .current.fast_render_timeframe - - page_is_visible: true, // when true, this page is visible - - auto_refresher_stop_until: 0, // timestamp in ms - used internally, to stop the - // auto-refresher for some time (when a chart is - // performing pan or zoom, we need to stop refreshing - // all other charts, to have the maximum speed for - // rendering the chart that is panned or zoomed). - // Used with .current.global_pan_sync_time - - on_scroll_refresher_stop_until: 0, // timestamp in ms - used to stop evaluating - // charts for some time, after a page scroll - - last_page_resize: Date.now(), // the timestamp of the last resize request - - last_page_scroll: 0, // the timestamp the last time the page was scrolled - - browser_timezone: 'unknown', // timezone detected by javascript - server_timezone: 'unknown', // timezone reported by the server - - force_data_points: 0, // force the number of points to be returned for charts - fake_chart_rendering: false, // when set to true, the dashboard will download data but will not render the charts - - passive_events: null, // true if the browser supports passive events - - // the current profile - // we may have many... - current: { - units: 'auto', // can be 'auto' or 'original' - temperature: 'celsius', // can be 'celsius' or 'fahrenheit' - seconds_as_time: true, // show seconds as DDd:HH:MM:SS ? - timezone: 'default', // the timezone to use, or 'default' - user_set_server_timezone: 'default', // as set by the user on the dashboard - - legend_toolbox: true, // show the legend toolbox on charts - resize_charts: true, // show the resize handler on charts - - pixels_per_point: isSlowDevice() ? 5 : 1, // the minimum pixels per point for all charts - // increase this to speed javascript up - // each chart library has its own limit too - // the max of this and the chart library is used - // the final is calculated every time, so a change - // here will have immediate effect on the next chart - // update - - idle_between_charts: 100, // ms - how much time to wait between chart updates - - fast_render_timeframe: 200, // ms - render continuously until this time of continuous - // rendering has been reached - // this setting is used to make it render e.g. 10 - // charts at once, sleep idle_between_charts time - // and continue for another 10 charts. - - idle_between_loops: 500, // ms - if all charts have been updated, wait this - // time before starting again. - - idle_parallel_loops: 100, // ms - the time between parallel refresher updates - - idle_lost_focus: 500, // ms - when the window does not have focus, check - // if focus has been regained, every this time - - global_pan_sync_time: 300, // ms - when you pan or zoom a chart, the background - // auto-refreshing of charts is paused for this amount - // of time - - sync_selection_delay: 400, // ms - when you pan or zoom a chart, wait this amount - // of time before setting up synchronized selections - // on hover. - - sync_selection: true, // enable or disable selection sync - - pan_and_zoom_delay: 50, // when panning or zooming, how ofter to update the chart - - sync_pan_and_zoom: true, // enable or disable pan and zoom sync - - pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming - - update_only_visible: true, // enable or disable visibility management / used for printing - - parallel_refresher: !isSlowDevice(), // enable parallel refresh of charts - - concurrent_refreshes: true, // when parallel_refresher is enabled, sync also the charts - - destroy_on_hide: isSlowDevice(), // destroy charts when they are not visible - - show_help: netdataShowHelp, // when enabled the charts will show some help - show_help_delay_show_ms: 500, - show_help_delay_hide_ms: 0, - - eliminate_zero_dimensions: true, // do not show dimensions with just zeros - - stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus - stop_updates_while_resizing: 1000, // ms - time to stop auto-refreshes while resizing the charts - - double_click_speed: 500, // ms - time between clicks / taps to detect double click/tap - - smooth_plot: !isSlowDevice(), // enable smooth plot, where possible - - color_fill_opacity_line: 1.0, - color_fill_opacity_area: 0.2, - color_fill_opacity_stacked: 0.8, - - pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox - pan_and_zoom_factor_multiplier_control: 2.0, - pan_and_zoom_factor_multiplier_shift: 3.0, - pan_and_zoom_factor_multiplier_alt: 4.0, - - abort_ajax_on_scroll: false, // kill pending ajax page scroll - async_on_scroll: false, // sync/async onscroll handler - onscroll_worker_duration_threshold: 30, // time in ms, for async scroll handler - - retries_on_data_failures: 3, // how many retries to make if we can't fetch chart data from the server - - setOptionCallback: function () { - } - }, - - debug: { - show_boxes: false, - main_loop: false, - focus: false, - visibility: false, - chart_data_url: false, - chart_errors: true, // remember to set it to false before merging - chart_timing: false, - chart_calls: false, - libraries: false, - dygraph: false, - globalSelectionSync: false, - globalPanAndZoom: false - } -}; - -NETDATA.statistics = { - refreshes_total: 0, - refreshes_active: 0, - refreshes_active_max: 0 -}; - -// local storage options - -NETDATA.localStorage = { - default: {}, - current: {}, - callback: {} // only used for resetting back to defaults -}; - -NETDATA.localStorageTested = -1; -NETDATA.localStorageTest = function () { - if (NETDATA.localStorageTested !== -1) { - return NETDATA.localStorageTested; - } - - if (typeof Storage !== "undefined" && typeof localStorage === 'object') { - let test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - NETDATA.localStorageTested = true; - } catch (e) { - NETDATA.localStorageTested = false; - } - } else { - NETDATA.localStorageTested = false; - } - - return NETDATA.localStorageTested; -}; - -NETDATA.localStorageGet = function (key, def, callback) { - let ret = def; - - if (typeof NETDATA.localStorage.default[key.toString()] === 'undefined') { - NETDATA.localStorage.default[key.toString()] = def; - NETDATA.localStorage.callback[key.toString()] = callback; - } - - if (NETDATA.localStorageTest()) { - try { - // console.log('localStorage: loading "' + key.toString() + '"'); - ret = localStorage.getItem(key.toString()); - // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString()); - if (ret === null || ret === 'undefined') { - // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"'); - localStorage.setItem(key.toString(), JSON.stringify(def)); - ret = def; - } else { - // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"'); - ret = JSON.parse(ret); - // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret)); - } - } catch (error) { - console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"'); - ret = def; - } - } - - if (typeof ret === 'undefined' || ret === 'undefined') { - console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret)); - ret = def; - } - - NETDATA.localStorage.current[key.toString()] = ret; - return ret; -}; - -NETDATA.localStorageSet = function (key, value, callback) { - if (typeof value === 'undefined' || value === 'undefined') { - console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value)); - } - - if (typeof NETDATA.localStorage.default[key.toString()] === 'undefined') { - NETDATA.localStorage.default[key.toString()] = value; - NETDATA.localStorage.current[key.toString()] = value; - NETDATA.localStorage.callback[key.toString()] = callback; - } - - if (NETDATA.localStorageTest()) { - // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"'); - try { - localStorage.setItem(key.toString(), JSON.stringify(value)); - } catch (e) { - console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"'); - } - } - - NETDATA.localStorage.current[key.toString()] = value; - return value; -}; - -NETDATA.localStorageGetRecursive = function (obj, prefix, callback) { - let keys = Object.keys(obj); - let len = keys.length; - while (len--) { - let i = keys[len]; - - if (typeof obj[i] === 'object') { - //console.log('object ' + prefix + '.' + i.toString()); - NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback); - continue; - } - - obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback); - } -}; - -NETDATA.setOption = function (key, value) { - if (key.toString() === 'setOptionCallback') { - if (typeof NETDATA.options.current.setOptionCallback === 'function') { - NETDATA.options.current[key.toString()] = value; - NETDATA.options.current.setOptionCallback(); - } - } else if (NETDATA.options.current[key.toString()] !== value) { - let name = 'options.' + key.toString(); - - if (typeof NETDATA.localStorage.default[name.toString()] === 'undefined') { - console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value); - } - - //console.log(NETDATA.localStorage); - //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()])); - //console.log(NETDATA.options); - NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null); - - if (typeof NETDATA.options.current.setOptionCallback === 'function') { - NETDATA.options.current.setOptionCallback(); - } - } - - return true; -}; - -NETDATA.getOption = function (key) { - return NETDATA.options.current[key.toString()]; -}; - -// read settings from local storage -NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null); - -// always start with this option enabled. -NETDATA.setOption('stop_updates_when_focus_is_lost', true); - -NETDATA.resetOptions = function () { - let keys = Object.keys(NETDATA.localStorage.default); - let len = keys.length; - - while (len--) { - let i = keys[len]; - let a = i.split('.'); - - if (a[0] === 'options') { - if (a[1] === 'setOptionCallback') { - continue; - } - if (typeof NETDATA.localStorage.default[i] === 'undefined') { - continue; - } - if (NETDATA.options.current[i] === NETDATA.localStorage.default[i]) { - continue; - } - - NETDATA.setOption(a[1], NETDATA.localStorage.default[i]); - } else if (a[0] === 'chart_heights') { - if (typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') { - NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]); - } - } - } - - NETDATA.dateTime.init(NETDATA.options.current.timezone); -}; - -// *** src/dashboard.js/timeout.js - -// TODO: Better name needed - -NETDATA.timeout = { - // by default, these are just wrappers to setTimeout() / clearTimeout() - - step: function (callback) { - return window.setTimeout(callback, 1000 / 60); - }, - - set: function (callback, delay) { - return window.setTimeout(callback, delay); - }, - - clear: function (id) { - return window.clearTimeout(id); - }, - - init: function () { - let custom = true; - - if (window.requestAnimationFrame) { - this.step = function (callback) { - return window.requestAnimationFrame(callback); - }; - - this.clear = function (handle) { - return window.cancelAnimationFrame(handle.value); - }; - // } else if (window.webkitRequestAnimationFrame) { - // this.step = function (callback) { - // return window.webkitRequestAnimationFrame(callback); - // }; - - // if (window.webkitCancelAnimationFrame) { - // this.clear = function (handle) { - // return window.webkitCancelAnimationFrame(handle.value); - // }; - // } else if (window.webkitCancelRequestAnimationFrame) { - // this.clear = function (handle) { - // return window.webkitCancelRequestAnimationFrame(handle.value); - // }; - // } - // } else if (window.mozRequestAnimationFrame) { - // this.step = function (callback) { - // return window.mozRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.mozCancelRequestAnimationFrame(handle.value); - // }; - // } else if (window.oRequestAnimationFrame) { - // this.step = function (callback) { - // return window.oRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.oCancelRequestAnimationFrame(handle.value); - // }; - // } else if (window.msRequestAnimationFrame) { - // this.step = function (callback) { - // return window.msRequestAnimationFrame(callback); - // }; - - // this.clear = function (handle) { - // return window.msCancelRequestAnimationFrame(handle.value); - // }; - } else { - custom = false; - } - - if (custom) { - // we have installed custom .step() / .clear() functions - // overwrite the .set() too - - this.set = function (callback, delay) { - let start = Date.now(), - handle = new Object(); - - const loop = () => { - let current = Date.now(), - delta = current - start; - - if (delta >= delay) { - callback.call(); - } else { - handle.value = this.step(loop); - } - } - - handle.value = this.step(loop); - return handle; - }; - } - } -}; - -NETDATA.timeout.init(); -// Codacy declarations -/* global netdataTheme */ - -NETDATA.themes = { - white: { - bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-3.3.7.css', - dashboard_css: NETDATA.serverStatic + 'dashboard.css?v20190902-0', - background: '#FFFFFF', - foreground: '#000000', - grid: '#F0F0F0', - axis: '#F0F0F0', - highlight: '#F5F5F5', - colors: ['#3366CC', '#DC3912', '#109618', '#FF9900', '#990099', '#DD4477', - '#3B3EAC', '#66AA00', '#0099C6', '#B82E2E', '#AAAA11', '#5574A6', - '#994499', '#22AA99', '#6633CC', '#E67300', '#316395', '#8B0707', - '#329262', '#3B3EAC'], - easypiechart_track: '#f0f0f0', - easypiechart_scale: '#dfe0e0', - gauge_pointer: '#C0C0C0', - gauge_stroke: '#F0F0F0', - gauge_gradient: false, - d3pie: { - title: '#333333', - subtitle: '#666666', - footer: '#888888', - other: '#aaaaaa', - mainlabel: '#333333', - percentage: '#dddddd', - value: '#aaaa22', - tooltip_bg: '#000000', - tooltip_fg: '#efefef', - segment_stroke: "#ffffff", - gradient_color: '#000000' - } - }, - slate: { - bootstrap_css: NETDATA.serverStatic + 'css/bootstrap-slate-flat-3.3.7.css?v20161229-1', - dashboard_css: NETDATA.serverStatic + 'dashboard.slate.css?v20190902-0', - background: '#272b30', - foreground: '#C8C8C8', - grid: '#283236', - axis: '#283236', - highlight: '#383838', - /* colors: [ '#55bb33', '#ff2222', '#0099C6', '#faa11b', '#adbce0', '#DDDD00', - '#4178ba', '#f58122', '#a5cc39', '#f58667', '#f5ef89', '#cf93c0', - '#a5d18a', '#b8539d', '#3954a3', '#c8a9cf', '#c7de8a', '#fad20a', - '#a6a479', '#a66da8' ], - */ - colors: ['#66AA00', '#FE3912', '#3366CC', '#D66300', '#0099C6', '#DDDD00', - '#5054e6', '#EE9911', '#BB44CC', '#e45757', '#ef0aef', '#CC7700', - '#22AA99', '#109618', '#905bfd', '#f54882', '#4381bf', '#ff3737', - '#329262', '#3B3EFF'], - easypiechart_track: '#373b40', - easypiechart_scale: '#373b40', - gauge_pointer: '#474b50', - gauge_stroke: '#373b40', - gauge_gradient: false, - d3pie: { - title: '#C8C8C8', - subtitle: '#283236', - footer: '#283236', - other: '#283236', - mainlabel: '#C8C8C8', - percentage: '#dddddd', - value: '#cccc44', - tooltip_bg: '#272b30', - tooltip_fg: '#C8C8C8', - segment_stroke: "#283236", - gradient_color: '#000000' - } - } -}; - -if (typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined') { - NETDATA.themes.current = NETDATA.themes[netdataTheme]; -} else { - NETDATA.themes.current = NETDATA.themes.white; -} - -NETDATA.colors = NETDATA.themes.current.colors; - -// these are the colors Google Charts are using -// we have them here to attempt emulate their look and feel on the other chart libraries -// http://there4.io/2012/05/02/google-chart-color-list/ -//NETDATA.colors = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6', -// '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11', -// '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ]; - -// an alternative set -// http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/ -// (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray) -//NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ]; -// dygraph - -// Codacy declarations -/* global smoothPlotter */ -/* global Dygraph */ - -NETDATA.dygraph = { - smooth: false -}; - -NETDATA.dygraphToolboxPanAndZoom = function (state, after, before) { - if (after < state.netdata_first) { - after = state.netdata_first; - } - - if (before > state.netdata_last) { - before = state.netdata_last; - } - - state.setMode('zoom'); - NETDATA.globalSelectionSync.stop(); - NETDATA.globalSelectionSync.delay(); - state.tmp.dygraph_user_action = true; - state.tmp.dygraph_force_zoom = true; - // state.log('toolboxPanAndZoom'); - state.updateChartPanOrZoom(after, before); - NETDATA.globalPanAndZoom.setMaster(state, after, before); -}; - -NETDATA.dygraphSetSelection = function (state, t) { - if (typeof state.tmp.dygraph_instance !== 'undefined') { - let r = state.calculateRowForTime(t); - if (r !== -1) { - state.tmp.dygraph_instance.setSelection(r); - return true; - } else { - state.tmp.dygraph_instance.clearSelection(); - state.legendShowUndefined(); - } - } - - return false; -}; - -NETDATA.dygraphClearSelection = function (state) { - if (typeof state.tmp.dygraph_instance !== 'undefined') { - state.tmp.dygraph_instance.clearSelection(); - } - return true; -}; - -NETDATA.dygraphSmoothInitialize = function (callback) { - $.ajax({ - url: NETDATA.dygraph_smooth_js, - cache: true, - dataType: "script", - xhrFields: {withCredentials: true} // required for the cookie - }) - .done(function () { - NETDATA.dygraph.smooth = true; - smoothPlotter.smoothing = 0.3; - }) - .fail(function () { - NETDATA.dygraph.smooth = false; - }) - .always(function () { - if (typeof callback === "function") { - return callback(); - } - }); -}; - -NETDATA.dygraphInitialize = function (callback) { - if (typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) { - $.ajax({ - url: NETDATA.dygraph_js, - cache: true, - dataType: "script", - xhrFields: {withCredentials: true} // required for the cookie - }) - .done(function () { - NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js); - }) - .fail(function () { - NETDATA.chartLibraries.dygraph.enabled = false; - NETDATA.error(100, NETDATA.dygraph_js); - }) - .always(function () { - if (NETDATA.chartLibraries.dygraph.enabled && NETDATA.options.current.smooth_plot) { - NETDATA.dygraphSmoothInitialize(callback); - } else if (typeof callback === "function") { - return callback(); - } - }); - } else { - NETDATA.chartLibraries.dygraph.enabled = false; - if (typeof callback === "function") { - return callback(); - } - } -}; - -NETDATA.dygraphChartUpdate = function (state, data) { - let dygraph = state.tmp.dygraph_instance; - - if (typeof dygraph === 'undefined') { - return NETDATA.dygraphChartCreate(state, data); - } - - // when the chart is not visible, and hidden - // if there is a window resize, dygraph detects - // its element size as 0x0. - // this will make it re-appear properly - - if (state.tm.last_unhidden > state.tmp.dygraph_last_rendered) { - dygraph.resize(); - } - - let options = { - file: data.result.data, - colors: state.chartColors(), - labels: data.result.labels, - //labelsDivWidth: state.chartWidth() - 70, - includeZero: state.tmp.dygraph_include_zero, - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names) - }; - - if (state.tmp.dygraph_chart_type === 'stacked') { - if (options.includeZero && state.dimensions_visibility.countSelected() < options.visibility.length) { - options.includeZero = 0; - } - } - - if (!NETDATA.chartLibraries.dygraph.isSparkline(state)) { - options.ylabel = state.units_current; // (state.units_desired === 'auto')?"":state.units_current; - } - - if (state.tmp.dygraph_force_zoom) { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() forced zoom update'); - } - - options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null; - //options.isZoomedIgnoreProgrammaticZoom = true; - state.tmp.dygraph_force_zoom = false; - } else if (state.current.name !== 'auto') { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() loose update'); - } - } else { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartUpdate() strict update'); - } - - options.dateWindow = (state.requested_padding !== null) ? [state.view_after, state.view_before] : null; - //options.isZoomedIgnoreProgrammaticZoom = true; - } - - options.valueRange = state.tmp.dygraph_options.valueRange; - - let oldMax = null, oldMin = null; - if (state.tmp.__commonMin !== null) { - state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; - oldMin = options.valueRange[0] = NETDATA.commonMin.get(state); - } - if (state.tmp.__commonMax !== null) { - state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; - oldMax = options.valueRange[1] = NETDATA.commonMax.get(state); - } - - if (state.tmp.dygraph_smooth_eligible) { - if ((NETDATA.options.current.smooth_plot && state.tmp.dygraph_options.plotter !== smoothPlotter) - || (NETDATA.options.current.smooth_plot === false && state.tmp.dygraph_options.plotter === smoothPlotter)) { - NETDATA.dygraphChartCreate(state, data); - return; - } - } - - if (netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(state) === false) { - // pan and zoom on snapshots - options.dateWindow = [NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms]; - //options.isZoomedIgnoreProgrammaticZoom = true; - } - - if (NETDATA.chartLibraries.dygraph.isLogScale(state)) { - if (Array.isArray(options.valueRange) && options.valueRange[0] <= 0) { - options.valueRange[0] = null; - } - } - - dygraph.updateOptions(options); - - let redraw = false; - if (oldMin !== null && oldMin > state.tmp.dygraph_instance.axes_[0].extremeRange[0]) { - state.data.min = state.tmp.dygraph_instance.axes_[0].extremeRange[0]; - options.valueRange[0] = NETDATA.commonMin.get(state); - redraw = true; - } - if (oldMax !== null && oldMax < state.tmp.dygraph_instance.axes_[0].extremeRange[1]) { - state.data.max = state.tmp.dygraph_instance.axes_[0].extremeRange[1]; - options.valueRange[1] = NETDATA.commonMax.get(state); - redraw = true; - } - - if (redraw) { - // state.log('forcing redraw to adapt to common- min/max'); - dygraph.updateOptions(options); - } - - state.tmp.dygraph_last_rendered = Date.now(); - return true; -}; - -NETDATA.dygraphChartCreate = function (state, data) { - if (NETDATA.options.debug.dygraph || state.debug) { - state.log('dygraphChartCreate()'); - } - - state.tmp.dygraph_chart_type = NETDATA.dataAttribute(state.element, 'dygraph-type', state.chart.chart_type); - if (state.tmp.dygraph_chart_type === 'stacked' && data.dimensions === 1) { - state.tmp.dygraph_chart_type = 'area'; - } - if (state.tmp.dygraph_chart_type === 'stacked' && NETDATA.chartLibraries.dygraph.isLogScale(state)) { - state.tmp.dygraph_chart_type = 'area'; - } - - let highlightCircleSize = NETDATA.chartLibraries.dygraph.isSparkline(state) ? 3 : 4; - - let smooth = NETDATA.dygraph.smooth - ? (NETDATA.dataAttributeBoolean(state.element, 'dygraph-smooth', (state.tmp.dygraph_chart_type === 'line' && NETDATA.chartLibraries.dygraph.isSparkline(state) === false))) - : false; - - state.tmp.dygraph_include_zero = NETDATA.dataAttribute(state.element, 'dygraph-includezero', (state.tmp.dygraph_chart_type === 'stacked')); - let drawAxis = NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawaxis', true); - - state.tmp.dygraph_options = { - colors: NETDATA.dataAttribute(state.element, 'dygraph-colors', state.chartColors()), - - // leave a few pixels empty on the right of the chart - rightGap: NETDATA.dataAttribute(state.element, 'dygraph-rightgap', 5), - showRangeSelector: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showrangeselector', false), - showRoller: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showroller', false), - title: NETDATA.dataAttribute(state.element, 'dygraph-title', state.title), - titleHeight: NETDATA.dataAttribute(state.element, 'dygraph-titleheight', 19), - legend: NETDATA.dataAttribute(state.element, 'dygraph-legend', 'always'), // we need this to get selection events - labels: data.result.labels, - labelsDiv: NETDATA.dataAttribute(state.element, 'dygraph-labelsdiv', state.element_legend_childs.hidden), - //labelsDivStyles: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivstyles', { 'fontSize':'1px' }), - //labelsDivWidth: NETDATA.dataAttribute(state.element, 'dygraph-labelsdivwidth', state.chartWidth() - 70), - labelsSeparateLines: NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsseparatelines', true), - labelsShowZeroValues: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-labelsshowzerovalues', true), - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: NETDATA.dataAttributeBoolean(state.element, 'dygraph-showlabelsonhighlight', true), - hideOverlayOnMouseOut: NETDATA.dataAttributeBoolean(state.element, 'dygraph-hideoverlayonmouseout', true), - includeZero: state.tmp.dygraph_include_zero, - xRangePad: NETDATA.dataAttribute(state.element, 'dygraph-xrangepad', 0), - yRangePad: NETDATA.dataAttribute(state.element, 'dygraph-yrangepad', 1), - valueRange: NETDATA.dataAttribute(state.element, 'dygraph-valuerange', [null, null]), - ylabel: state.units_current, // (state.units_desired === 'auto')?"":state.units_current, - yLabelWidth: NETDATA.dataAttribute(state.element, 'dygraph-ylabelwidth', 12), - - // the function to plot the chart - plotter: null, - - // The width of the lines connecting data points. - // This can be used to increase the contrast or some graphs. - strokeWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokewidth', ((state.tmp.dygraph_chart_type === 'stacked') ? 0.1 : ((smooth === true) ? 1.5 : 0.7))), - strokePattern: NETDATA.dataAttribute(state.element, 'dygraph-strokepattern', undefined), - - // The size of the dot to draw on each point in pixels (see drawPoints). - // A dot is always drawn when a point is "isolated", - // i.e. there is a missing point on either side of it. - // This also controls the size of those dots. - drawPoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawpoints', false), - - // Draw points at the edges of gaps in the data. - // This improves visibility of small data segments or other data irregularities. - drawGapEdgePoints: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgapedgepoints', true), - connectSeparatedPoints: NETDATA.chartLibraries.dygraph.isLogScale(state) ? false : NETDATA.dataAttributeBoolean(state.element, 'dygraph-connectseparatedpoints', false), - pointSize: NETDATA.dataAttribute(state.element, 'dygraph-pointsize', 1), - - // enabling this makes the chart with little square lines - stepPlot: NETDATA.dataAttributeBoolean(state.element, 'dygraph-stepplot', false), - - // Draw a border around graph lines to make crossing lines more easily - // distinguishable. Useful for graphs with many lines. - strokeBorderColor: NETDATA.dataAttribute(state.element, 'dygraph-strokebordercolor', NETDATA.themes.current.background), - strokeBorderWidth: NETDATA.dataAttribute(state.element, 'dygraph-strokeborderwidth', (state.tmp.dygraph_chart_type === 'stacked') ? 0.0 : 0.0), - fillGraph: NETDATA.dataAttribute(state.element, 'dygraph-fillgraph', (state.tmp.dygraph_chart_type === 'area' || state.tmp.dygraph_chart_type === 'stacked')), - fillAlpha: NETDATA.dataAttribute(state.element, 'dygraph-fillalpha', - ((state.tmp.dygraph_chart_type === 'stacked') - ? NETDATA.options.current.color_fill_opacity_stacked - : NETDATA.options.current.color_fill_opacity_area) - ), - stackedGraph: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraph', (state.tmp.dygraph_chart_type === 'stacked')), - stackedGraphNaNFill: NETDATA.dataAttribute(state.element, 'dygraph-stackedgraphnanfill', 'none'), - drawAxis: drawAxis, - axisLabelFontSize: NETDATA.dataAttribute(state.element, 'dygraph-axislabelfontsize', 10), - axisLineColor: NETDATA.dataAttribute(state.element, 'dygraph-axislinecolor', NETDATA.themes.current.axis), - axisLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-axislinewidth', 1.0), - drawGrid: NETDATA.dataAttributeBoolean(state.element, 'dygraph-drawgrid', true), - gridLinePattern: NETDATA.dataAttribute(state.element, 'dygraph-gridlinepattern', null), - gridLineWidth: NETDATA.dataAttribute(state.element, 'dygraph-gridlinewidth', 1.0), - gridLineColor: NETDATA.dataAttribute(state.element, 'dygraph-gridlinecolor', NETDATA.themes.current.grid), - maxNumberWidth: NETDATA.dataAttribute(state.element, 'dygraph-maxnumberwidth', 8), - sigFigs: NETDATA.dataAttribute(state.element, 'dygraph-sigfigs', null), - digitsAfterDecimal: NETDATA.dataAttribute(state.element, 'dygraph-digitsafterdecimal', 2), - valueFormatter: NETDATA.dataAttribute(state.element, 'dygraph-valueformatter', undefined), - highlightCircleSize: NETDATA.dataAttribute(state.element, 'dygraph-highlightcirclesize', highlightCircleSize), - highlightSeriesOpts: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesopts', null), // TOO SLOW: { strokeWidth: 1.5 }, - highlightSeriesBackgroundAlpha: NETDATA.dataAttribute(state.element, 'dygraph-highlightseriesbackgroundalpha', null), // TOO SLOW: (state.tmp.dygraph_chart_type === 'stacked')?0.7:0.5, - pointClickCallback: NETDATA.dataAttribute(state.element, 'dygraph-pointclickcallback', undefined), - visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names), - logscale: NETDATA.chartLibraries.dygraph.isLogScale(state) ? 'y' : undefined, - - // Expects a string in the format ":
You must enable JavaScript in order to use Netdata!
You can do this in your browser settings.
Filename
Hostname
Origin URL
Charts Info
Snapshot Info
Time Range
Comments

\ No newline at end of file diff --git a/luci-app-netdata/web/main.js b/luci-app-netdata/web/main.js deleted file mode 100644 index 8ecc86995..000000000 --- a/luci-app-netdata/web/main.js +++ /dev/null @@ -1,5159 +0,0 @@ -// Main JavaScript file for the Netdata GUI. -// 翻译 By Jason -// Codacy declarations -/* global NETDATA */ - -// netdata snapshot data -var netdataSnapshotData = null; - -// enable alarms checking and notifications -var netdataShowAlarms = true; - -// enable registry updates -var netdataRegistry = true; - -// forward definition only - not used here -var netdataServer = undefined; -var netdataServerStatic = undefined; -var netdataCheckXSS = undefined; - -// control the welcome modal and analytics -var this_is_demo = null; - -function escapeUserInputHTML(s) { - return s.toString() - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/#/g, '#') - .replace(/'/g, ''') - .replace(/\(/g, '(') - .replace(/\)/g, ')') - .replace(/\//g, '/'); -} - -function verifyURL(s) { - if (typeof (s) === 'string' && (s.startsWith('http://') || s.startsWith('https://'))) { - return s - .replace(/'/g, '%22') - .replace(/"/g, '%27') - .replace(/\)/g, '%28') - .replace(/\(/g, '%29'); - } - - console.log('invalid URL detected:'); - console.log(s); - return 'javascript:alert("invalid url");'; -} - -// -------------------------------------------------------------------- -// urlOptions - -var urlOptions = { - hash: '#', - theme: null, - help: null, - mode: 'live', // 'live', 'print' - update_always: false, - pan_and_zoom: false, - server: null, - after: 0, - before: 0, - highlight: false, - highlight_after: 0, - highlight_before: 0, - nowelcome: false, - show_alarms: false, - chart: null, - family: null, - alarm: null, - alarm_unique_id: 0, - alarm_id: 0, - alarm_event_id: 0, - alarm_when: 0, - - hasProperty: function (property) { - // console.log('checking property ' + property + ' of type ' + typeof(this[property])); - return typeof this[property] !== 'undefined'; - }, - - genHash: function (forReload) { - var hash = urlOptions.hash; - - if (urlOptions.pan_and_zoom === true) { - hash += ';after=' + urlOptions.after.toString() + - ';before=' + urlOptions.before.toString(); - } - - if (urlOptions.highlight === true) { - hash += ';highlight_after=' + urlOptions.highlight_after.toString() + - ';highlight_before=' + urlOptions.highlight_before.toString(); - } - - if (urlOptions.theme !== null) { - hash += ';theme=' + urlOptions.theme.toString(); - } - - if (urlOptions.help !== null) { - hash += ';help=' + urlOptions.help.toString(); - } - - if (urlOptions.update_always === true) { - hash += ';update_always=true'; - } - - if (forReload === true && urlOptions.server !== null) { - hash += ';server=' + urlOptions.server.toString(); - } - - if (urlOptions.mode !== 'live') { - hash += ';mode=' + urlOptions.mode; - } - - return hash; - }, - - 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]; - } - } - } - - var booleans = ['nowelcome', 'show_alarms', '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; - } - } - - var numeric = ['after', 'before', 'highlight_after', 'highlight_before', 'alarm_when']; - len = numeric.length; - while (len--) { - if (typeof urlOptions[numeric[len]] === 'string') { - try { - urlOptions[numeric[len]] = parseInt(urlOptions[numeric[len]]); - } - catch (e) { - console.log('failed to parse URL hash parameter ' + numeric[len]); - urlOptions[numeric[len]] = 0; - } - } - } - - if (urlOptions.alarm_when) { - // if alarm_when exists, create after/before params - // -/+ 2 minutes from the alarm, and reload the page - const alarmTime = new Date(urlOptions.alarm_when * 1000).valueOf(); - const timeMarginMs = 120000; // 2 mins - - const after = alarmTime - timeMarginMs; - const before = alarmTime + timeMarginMs; - const newHash = document.location.hash.replace( - /;alarm_when=[0-9]*/i, - ";after=" + after + ";before=" + before, - ); - history.replaceState(null, '', newHash); - location.reload(); - } - - if (urlOptions.server !== null && urlOptions.server !== '') { - netdataServerStatic = document.location.origin.toString() + document.location.pathname.toString(); - netdataServer = urlOptions.server; - netdataCheckXSS = true; - } else { - urlOptions.server = null; - } - - if (urlOptions.before > 0 && urlOptions.after > 0) { - urlOptions.pan_and_zoom = true; - urlOptions.nowelcome = true; - } else { - urlOptions.pan_and_zoom = false; - } - - if (urlOptions.highlight_before > 0 && urlOptions.highlight_after > 0) { - urlOptions.highlight = true; - } else { - urlOptions.highlight = false; - } - - switch (urlOptions.mode) { - case 'print': - urlOptions.theme = 'white'; - urlOptions.welcome = false; - urlOptions.help = false; - urlOptions.show_alarms = false; - - if (urlOptions.pan_and_zoom === false) { - urlOptions.pan_and_zoom = true; - urlOptions.before = Date.now(); - urlOptions.after = urlOptions.before - 600000; - } - - netdataShowAlarms = false; - netdataRegistry = false; - this_is_demo = false; - break; - - case 'live': - default: - urlOptions.mode = 'live'; - break; - } - - // console.log(urlOptions); - }, - - hashUpdate: function () { - history.replaceState(null, '', urlOptions.genHash(true)); - }, - - netdataPanAndZoomCallback: function (status, after, before) { - //console.log(1); - //console.log(new Error().stack); - - if (netdataSnapshotData === null) { - urlOptions.pan_and_zoom = status; - urlOptions.after = after; - urlOptions.before = before; - urlOptions.hashUpdate(); - } - }, - - netdataHighlightCallback: function (status, after, before) { - //console.log(2); - //console.log(new Error().stack); - - if (status === true && (after === null || before === null || after <= 0 || before <= 0 || after >= before)) { - status = false; - after = 0; - before = 0; - } - - if (netdataSnapshotData === null) { - urlOptions.highlight = status; - } else { - urlOptions.highlight = false; - } - - urlOptions.highlight_after = Math.round(after); - urlOptions.highlight_before = Math.round(before); - urlOptions.hashUpdate(); - - var show_eye = NETDATA.globalChartUnderlay.hasViewport(); - - if (status === true && after > 0 && before > 0 && after < before) { - var d1 = NETDATA.dateTime.localeDateString(after); - var d2 = NETDATA.dateTime.localeDateString(before); - if (d1 === d2) { - d2 = ''; - } - document.getElementById('navbar-highlight-content').innerHTML = - ((show_eye === true) ? '' : '').toString() - + 'highlighted time-frame' - + ' ' + d1 + ' ' + NETDATA.dateTime.localeTimeString(after) + ' to ' - + ' ' + d2 + ' ' + NETDATA.dateTime.localeTimeString(before) + ', ' - + 'duration ' + NETDATA.seconds4human(Math.round((before - after) / 1000)) + '' - + '' - + ''; - - $('.navbar-highlight').show(); - - $('.highlight-tooltip').tooltip({ - html: true, - delay: { show: 500, hide: 0 }, - container: 'body' - }); - } else { - $('.navbar-highlight').hide(); - } - }, - - clearHighlight: function () { - NETDATA.globalChartUnderlay.clear(); - - if (NETDATA.globalPanAndZoom.isActive() === true) { - NETDATA.globalPanAndZoom.clearMaster(); - } - }, - - showHighlight: function () { - NETDATA.globalChartUnderlay.focus(); - } -}; - -urlOptions.parseHash(); - -// -------------------------------------------------------------------- -// check options that should be processed before loading netdata.js - -var localStorageTested = -1; - -function localStorageTest() { - if (localStorageTested !== -1) { - return localStorageTested; - } - - if (typeof Storage !== "undefined" && typeof localStorage === 'object') { - var test = 'test'; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - localStorageTested = true; - } - catch (e) { - console.log(e); - localStorageTested = false; - } - } else { - localStorageTested = false; - } - - return localStorageTested; -} - -function loadLocalStorage(name) { - var ret = null; - - try { - if (localStorageTest() === true) { - ret = localStorage.getItem(name); - } else { - console.log('localStorage is not available'); - } - } - catch (error) { - console.log(error); - return null; - } - - if (typeof ret === 'undefined' || ret === null) { - return null; - } - - // console.log('loaded: ' + name.toString() + ' = ' + ret.toString()); - - return ret; -} - -function saveLocalStorage(name, value) { - // console.log('saving: ' + name.toString() + ' = ' + value.toString()); - try { - if (localStorageTest() === true) { - localStorage.setItem(name, value.toString()); - return true; - } - } - catch (error) { - console.log(error); - } - - return false; -} - -function getTheme(def) { - if (urlOptions.mode === 'print') { - return 'white'; - } - - var ret = loadLocalStorage('netdataTheme'); - if (typeof ret === 'undefined' || ret === null || ret === 'undefined') { - return def; - } else { - return ret; - } -} - -function setTheme(theme) { - if (urlOptions.mode === 'print') { - return false; - } - - if (theme === netdataTheme) { - return false; - } - return saveLocalStorage('netdataTheme', theme); -} - -var netdataTheme = getTheme('slate'); -var netdataShowHelp = true; - -if (urlOptions.theme !== null) { - setTheme(urlOptions.theme); - netdataTheme = urlOptions.theme; -} else { - urlOptions.theme = netdataTheme; -} - -if (urlOptions.help !== null) { - saveLocalStorage('options.show_help', urlOptions.help); - netdataShowHelp = urlOptions.help; -} else { - urlOptions.help = loadLocalStorage('options.show_help'); -} - -// -------------------------------------------------------------------- -// natural sorting -// http://www.davekoelle.com/files/alphanum.js - LGPL - -function naturalSortChunkify(t) { - var tz = []; - var x = 0, y = -1, n = 0, i, j; - - while (i = (j = t.charAt(x++)).charCodeAt(0)) { - var m = (i >= 48 && i <= 57); - if (m !== n) { - tz[++y] = ""; - n = m; - } - tz[y] += j; - } - - return tz; -} - -function naturalSortCompare(a, b) { - var aa = naturalSortChunkify(a.toLowerCase()); - var bb = naturalSortChunkify(b.toLowerCase()); - - for (var x = 0; aa[x] && bb[x]; x++) { - if (aa[x] !== bb[x]) { - var c = Number(aa[x]), d = Number(bb[x]); - if (c.toString() === aa[x] && d.toString() === bb[x]) { - return c - d; - } else { - return (aa[x] > bb[x]) ? 1 : -1; - } - } - } - - return aa.length - bb.length; -} - -// -------------------------------------------------------------------- -// saving files to client - -function saveTextToClient(data, filename) { - var blob = new Blob([data], { - type: 'application/octet-stream' - }); - - var url = URL.createObjectURL(blob); - var link = document.createElement('a'); - link.setAttribute('href', url); - link.setAttribute('download', filename); - - var el = document.getElementById('hiddenDownloadLinks'); - el.innerHTML = ''; - el.appendChild(link); - - setTimeout(function () { - el.removeChild(link); - URL.revokeObjectURL(url); - }, 60); - - link.click(); -} - -function saveObjectToClient(data, filename) { - saveTextToClient(JSON.stringify(data), filename); -} - -// ----------------------------------------------------------------------------- -// registry call back to render my-netdata menu - -function toggleExpandIcon(svgEl) { - if (svgEl.getAttribute('data-icon') === 'caret-down') { - svgEl.setAttribute('data-icon', 'caret-up'); - } else { - svgEl.setAttribute('data-icon', 'caret-down'); - } -} - -function toggleAgentItem(e, guid) { - e.stopPropagation(); - e.preventDefault(); - - toggleExpandIcon(e.currentTarget.children[0]); - - const el = document.querySelector(`.agent-alternate-urls.agent-${guid}`); - if (el) { - el.classList.toggle('collapsed'); - } -} - -// When you stream metrics from netdata to netdata, the receiving netdata now -// has multiple host databases. It's own, and multiple mirrored. Mirrored databases -// can be accessed with -const OLD_DASHBOARD_SUFFIX = "old" -let isOldSuffix = true -try { - const currentScriptMainJs = document.currentScript; - const mainJsSrc = currentScriptMainJs.getAttribute("src") - isOldSuffix = mainJsSrc.startsWith("../main.js") -} catch { - console.warn("current script not detecting, assuming the dashboard is running with /old suffix") -} - -function transformWithOldSuffix(url) { - return isOldSuffix ? `../${url}` : url -} - -function renderStreamedHosts(options) { - let html = `
Databases streamed to this agent
`; - - var base = document.location.origin.toString() + - document.location.pathname.toString() - .replace(isOldSuffix ? `/${OLD_DASHBOARD_SUFFIX}` : "", ""); - if (base.endsWith("/host/" + options.hostname + "/")) { - base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length); - } - - if (base.endsWith("/")) { - base = base.substring(0, base.length - 1); - } - - var master = options.hosts[0].hostname; - // We sort a clone of options.hosts, to keep the master as the first element - // for future calls. - var sorted = options.hosts.slice(0).sort(function (a, b) { - if (a.hostname === master) { - return -1; - } - return naturalSortCompare(a.hostname, b.hostname); - }); - - let displayedDatabases = false; - - for (var s of sorted) { - let url, icon; - const hostname = s.hostname; - - if (myNetdataMenuFilterValue !== "") { - if (!hostname.includes(myNetdataMenuFilterValue)) { - continue; - } - } - - displayedDatabases = true; - - if (hostname === master) { - url = isOldSuffix ? `${base}/${OLD_DASHBOARD_SUFFIX}/` : `${base}/`; - icon = 'home'; - } else { - url = isOldSuffix ? `${base}/host/${hostname}/${OLD_DASHBOARD_SUFFIX}/` : `${base}/host/${hostname}/`; - icon = 'window-restore'; - } - - html += ( - `
- - - - - ${hostname} - -
-
` - ) - } - - if (!displayedDatabases) { - html += ( - `
- - no databases match the filter criteria. -
` - ) - } - - return html; -} - -function renderMachines(machinesArray) { - let html = `
My nodes
`; - - if (machinesArray === null) { - let ret = loadLocalStorage("registryCallback"); - if (ret) { - machinesArray = JSON.parse(ret); - console.log("failed to contact the registry - loaded registry data from browser local storage"); - } - } - - let found = false; - let displayedAgents = false; - - const maskedURL = NETDATA.registry.MASKED_DATA; - - if (machinesArray) { - saveLocalStorage("registryCallback", JSON.stringify(machinesArray)); - - var machines = machinesArray.sort(function (a, b) { - return naturalSortCompare(a.name, b.name); - }); - - for (var machine of machines) { - found = true; - - if (myNetdataMenuFilterValue !== "") { - if (!machine.name.includes(myNetdataMenuFilterValue)) { - continue; - } - } - - displayedAgents = true; - - const alternateUrlItems = ( - `` - ) - - html += ( - ` - ${alternateUrlItems}` - ) - } - - if (found && (!displayedAgents)) { - html += ( - `
- - zero nodes are matching the filter value. -
` - ) - } - } - - if (!found) { - if (machines) { - html += ( - `` - ) - } else { - html += ( - `` - ) - } - - html += `
`; - html += `
Demo netdata nodes
`; - - const demoServers = [ - { url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)" }, - { url: "//newyork.netdata.rocks/default.html", title: "US - New York (DigitalOcean.com)" }, - { url: "//sanfrancisco.netdata.rocks/default.html", title: "US - San Francisco (DigitalOcean.com)" }, - { url: "//atlanta.netdata.rocks/default.html", title: "US - Atlanta (CDN77.com)" }, - { url: "//frankfurt.netdata.rocks/default.html", title: "Germany - Frankfurt (DigitalOcean.com)" }, - { url: "//toronto.netdata.rocks/default.html", title: "Canada - Toronto (DigitalOcean.com)" }, - { url: "//singapore.netdata.rocks/default.html", title: "Japan - Singapore (DigitalOcean.com)" }, - { url: "//bangalore.netdata.rocks/default.html", title: "India - Bangalore (DigitalOcean.com)" }, - - ] - - for (var server of demoServers) { - html += ( - `
- - ${server.title} -
-
- ` - ); - } - } - - return html; -} - -function setMyNetdataMenu(html) { - const el = document.getElementById('my-netdata-dropdown-content') - el.innerHTML = html; -} - -function clearMyNetdataMenu() { - setMyNetdataMenu(`
- - Loading, please wait... -
-
`); -} - -function errorMyNetdataMenu() { - setMyNetdataMenu(`
- - Cannot load known Netdata agents from Netdata Cloud! Please make sure you have the latest version of Netdata. -
`); -} - -function restrictMyNetdataMenu() { - setMyNetdataMenu(`
- Please sign in to netdata.cloud to view your nodes! -
-
`); -} - -function openAuthenticatedUrl(url) { - if (isSignedIn()) { - window.open(url); - } else { - window.open(`${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}&redirect_uri=${encodeURIComponent(window.location.origin + "/" + url)}`); - } -} - -function renderMyNetdataMenu(machinesArray) { - const el = document.getElementById('my-netdata-dropdown-content'); - el.classList.add(`theme-${netdataTheme}`); - - if (machinesArray == registryAgents) { - console.log("Rendering my-netdata menu from registry"); - } else { - console.log("Rendering my-netdata menu from netdata.cloud", machinesArray); - } - - let html = ''; - - if (!isSignedIn()) { - if (!NETDATA.registry.isRegistryEnabled()) { - html += ( - `` - ); - } - } - - if (isSignedIn()) { - html += ( - `
- - -
-
` - ); - } - - // options.hosts = [ - // { - // hostname: "streamed1", - // }, - // { - // hostname: "streamed2", - // }, - // ] - - if (options.hosts.length > 1) { - html += `
${renderStreamedHosts(options)}

`; - } - - if (isSignedIn() || NETDATA.registry.isRegistryEnabled()) { - html += `
${renderMachines(machinesArray)}

`; - } - - if (!isSignedIn()) { - html += ( - `
- - 更换标识 -
-
-
- - 这是什么? -
-
` - ) - } else { - html += ( - `
- - Nodes beta -
-
- -
- - What is this? -
-
` - ) - } - - el.innerHTML = html; - - gotoServerInit(); -} - -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('.netdata.ai') || - document.location.hostname.endsWith('.netdata.live') || - document.location.hostname.endsWith('.firehol.org') || - document.location.hostname.endsWith('.netdata.online') || - document.location.hostname.endsWith('.netdata.cloud')) { - this_is_demo = true; - } - } - } - catch (error) { - } - return this_is_demo; -} - -function netdataURL(url, forReload) { - if (typeof url === 'undefined') - // url = document.location.toString(); - { - url = ''; - } - - if (url.indexOf('#') !== -1) { - url = url.substring(0, url.indexOf('#')); - } - - var hash = urlOptions.genHash(forReload); - - // console.log('netdataURL: ' + url + hash); - - return url + hash; -} - -function netdataReload(url) { - document.location = verifyURL(netdataURL(url, true)); - - // since we play with hash - // this is needed to reload the page - location.reload(); -} - -function gotoHostedModalHandler(url) { - document.location = verifyURL(url + urlOptions.genHash()); - return false; -} - -var gotoServerValidateRemaining = 0; -var gotoServerMiddleClick = false; -var gotoServerStop = false; - -function gotoServerValidateUrl(id, guid, url) { - var penalty = 0; - var error = 'failed'; - - 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. - { - penalty = 500; - } else if (document.location.toString().startsWith('https://') && url.toString().startsWith('http://')) { - error = 'can\'t check'; - } - - var finalURL = netdataURL(url); - - setTimeout(function () { - document.getElementById('gotoServerList').innerHTML += '' + escapeUserInputHTML(url) + 'checking...'; - - 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"; - - if (!gotoServerStop) { - gotoServerStop = true; - - if (gotoServerMiddleClick) { - window.open(verifyURL(finalURL), '_blank'); - gotoServerMiddleClick = false; - document.getElementById('gotoServerResponse').innerHTML = 'Opening new window to ' + NETDATA.registry.machines[guid].name + '
' + escapeUserInputHTML(url) + '

(check your pop-up blocker if it fails)'; - } else { - document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:
' + escapeUserInputHTML(url) + ''; - document.location = verifyURL(finalURL); - $('#gotoServerModal').modal('hide'); - } - } - } 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 = 'Sorry! I cannot find any operational URL for this server'; - } - } - }); - }, (id * 50) + penalty); -} - -function gotoServerModalHandler(guid) { - // console.log('goto server: ' + guid); - - gotoServerStop = false; - var checked = {}; - var len = NETDATA.registry.machines[guid].alternate_urls.length; - var count = 0; - - document.getElementById('gotoServerResponse').innerHTML = ''; - document.getElementById('gotoServerList').innerHTML = ''; - document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name; - $('#gotoServerModal').modal('show'); - - gotoServerValidateRemaining = len; - while (len--) { - var url = NETDATA.registry.machines[guid].alternate_urls[len]; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); - } - - if (!isSignedIn()) { - // When the registry is enabled, if the user's known URLs are not working - // we consult the registry to get additional URLs. - setTimeout(function () { - if (gotoServerStop === false) { - document.getElementById('gotoServerResponse').innerHTML = 'Added all the known URLs for this machine.'; - 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 { - gotoServerMiddleClick = false; - } - - return true; - }); -} - -function switchRegistryModalHandler() { - document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid; - document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server; - document.getElementById('switchRegistryResponse').innerHTML = ''; - $('#switchRegistryModal').modal('show'); -} - -function notifyForSwitchRegistry() { - var n = document.getElementById('switchRegistryPersonGUID').value; - - 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 = "Sorry! The registry rejected your request."; - } - }); - } else { - document.getElementById('switchRegistryResponse').innerHTML = "The ID you have entered is not a GUID."; - } -} - -var deleteRegistryGuid = null; -var deleteRegistryUrl = null; - -function deleteRegistryModalHandler(guid, name, url) { - // void (guid); - - deleteRegistryGuid = guid; - deleteRegistryUrl = url; - - document.getElementById('deleteRegistryServerName').innerHTML = name; - document.getElementById('deleteRegistryServerName2').innerHTML = name; - document.getElementById('deleteRegistryServerURL').innerHTML = url; - document.getElementById('deleteRegistryResponse').innerHTML = ''; - - $('#deleteRegistryModal').modal('show'); -} - -function notifyForDeleteRegistry() { - const responseEl = document.getElementById('deleteRegistryResponse'); - - if (deleteRegistryUrl) { - if (isSignedIn()) { - deleteCloudAgentURL(deleteRegistryGuid, deleteRegistryUrl) - .then((count) => { - if (!count) { - responseEl.innerHTML = "Sorry, this command was rejected by netdata.cloud!"; - return; - } - NETDATA.registry.delete(deleteRegistryUrl, function (result) { - if (result === null) { - console.log("Received error from registry", result); - } - - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - }); - }); - } else { - NETDATA.registry.delete(deleteRegistryUrl, function (result) { - if (result !== null) { - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - } else { - responseEl.innerHTML = "Sorry, this command was rejected by the registry server!"; - } - }); - } - } -} - -var options = { - menus: {}, - submenu_names: {}, - data: null, - hostname: 'netdata_server', // will be overwritten by the netdata server - version: 'unknown', - release_channel: 'unknown', - hosts: [], - - duration: 0, // the default duration of the charts - update_every: 1, - - chartsPerRow: 0, - // chartsMinWidth: 1450, - chartsHeight: 180, -}; - -function chartsPerRow(total) { - void (total); - - if (options.chartsPerRow === 0) { - return 1; - //var width = Math.floor(total / options.chartsMinWidth); - //if(width === 0) width = 1; - //return width; - } else { - return options.chartsPerRow; - } -} - -function prioritySort(a, b) { - if (a.priority < b.priority) { - return -1; - } - if (a.priority > b.priority) { - return 1; - } - return naturalSortCompare(a.name, b.name); -} - -function sortObjectByPriority(object) { - var idx = {}; - var sorted = []; - - for (var i in object) { - if (!object.hasOwnProperty(i)) { - continue; - } - - if (typeof idx[i] === 'undefined') { - idx[i] = object[i]; - sorted.push(i); - } - } - - sorted.sort(function (a, b) { - if (idx[a].priority < idx[b].priority) { - return -1; - } - if (idx[a].priority > idx[b].priority) { - return 1; - } - return naturalSortCompare(a, b); - }); - - return sorted; -} - -// ---------------------------------------------------------------------------- -// scroll to a section, without changing the browser history - -function scrollToId(hash) { - if (hash && hash !== '' && document.getElementById(hash) !== null) { - var offset = $('#' + hash).offset(); - if (typeof offset !== 'undefined') { - //console.log('scrolling to ' + hash + ' at ' + offset.top.toString()); - $('html, body').animate({ scrollTop: offset.top - 30 }, 0); - } - } - - // we must return false to prevent the default action - return false; -} - -// ---------------------------------------------------------------------------- - -// user editable information -var customDashboard = { - menu: {}, - submenu: {}, - context: {} -}; - -// netdata standard information -var netdataDashboard = { - sparklines_registry: {}, - os: 'unknown', - - menu: {}, - submenu: {}, - context: {}, - - // generate a sparkline - // used in the documentation - sparkline: function (prefix, chart, dimension, units, suffix) { - if (options.data === null || typeof options.data.charts === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart] === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart].dimensions === 'undefined') { - return ''; - } - - if (typeof options.data.charts[chart].dimensions[dimension] === 'undefined') { - return ''; - } - - var key = chart + '.' + dimension; - - if (typeof units === 'undefined') { - units = ''; - } - - if (typeof this.sparklines_registry[key] === 'undefined') { - this.sparklines_registry[key] = { count: 1 }; - } else { - this.sparklines_registry[key].count++; - } - - key = key + '.' + this.sparklines_registry[key].count; - - return prefix + '
(X' + units + ')' + suffix; - }, - - gaugeChart: function (title, width, dimensions, colors) { - if (typeof colors === 'undefined') { - colors = ''; - } - - if (typeof dimensions === 'undefined') { - dimensions = ''; - } - - return '
'; - }, - - anyAttribute: function (obj, attr, key, def) { - if (typeof (obj[key]) !== 'undefined') { - var x = obj[key][attr]; - - if (typeof (x) === 'undefined') { - return def; - } - - if (typeof (x) === 'function') { - return x(netdataDashboard.os); - } - - return x; - } - - return def; - }, - - menuTitle: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() - + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); - } - - return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); - }, - - menuIcon: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '').toString(); - } - - return this.anyAttribute(this.menu, 'icon', chart.menu, ''); - }, - - menuInfo: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null); - } - - return this.anyAttribute(this.menu, 'info', chart.menu, null); - }, - - menuHeight: function (chart) { - if (typeof chart.menu_pattern !== 'undefined') { - return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0); - } - - return this.anyAttribute(this.menu, 'height', chart.menu, 1.0); - }, - - submenuTitle: function (menu, submenu) { - var key = menu + '.' + submenu; - // console.log(key); - var title = this.anyAttribute(this.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 this.anyAttribute(this.submenu, 'info', key, null); - }, - - submenuHeight: function (menu, submenu, relative) { - var key = menu + '.' + submenu; - return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative; - }, - - contextInfo: function (id) { - var x = this.anyAttribute(this.context, 'info', id, null); - - if (x !== null) { - return '
' + x + '
'; - } else { - return ''; - } - }, - - contextValueRange: function (id) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined') { - return this.context[id].valueRange; - } else { - return '[null, null]'; - } - }, - - contextHeight: function (id, def) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined') { - return def * this.context[id].height; - } else { - return def; - } - }, - - contextDecimalDigits: function (id, def) { - if (typeof this.context[id] !== 'undefined' && typeof this.context[id].decimalDigits !== 'undefined') { - return this.context[id].decimalDigits; - } else { - return def; - } - } -}; - -// ---------------------------------------------------------------------------- - -// enrich the data structure returned by netdata -// to reflect our menu system and content -// TODO: this is a shame - we should fix charts naming (issue #807) -function enrichChartData(chart) { - var parts = chart.type.split('_'); - var tmp = parts[0]; - - switch (tmp) { - case 'ap': - case 'net': - case 'disk': - case 'powersupply': - case 'statsd': - chart.menu = tmp; - break; - - case 'apache': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'cache') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'bind': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'rndc') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = 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 'go': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'expvar') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'mount': - if (parts.length > 2) { - chart.menu = tmp + '_' + parts[1]; - } else { - chart.menu = tmp; - } - break; - - case 'isc': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'dhcpd') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'ovpn': - chart.menu = chart.type; - if (parts.length > 3 && parts[1] === 'status' && parts[2] === 'log') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - - case 'smartd': - case 'web': - chart.menu = chart.type; - if (parts.length > 2 && parts[1] === 'log') { - chart.menu_pattern = tmp + '_' + parts[1]; - } else if (parts.length > 1) { - 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 (chart.context === 'tc.qos' && (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); - } else { - options.submenu_names[chart.family] = n; - } - } - - // increase the priority of IFB devices - // to have inbound appear before outbound - if (chart.id.match(/.*-ifb$/)) { - chart.priority--; - } - - break; - - default: - chart.menu = chart.type; - if (parts.length > 1) { - chart.menu_pattern = tmp; - } - break; - } - - chart.submenu = chart.family; -} - -// ---------------------------------------------------------------------------- - -function headMain(os, charts, duration) { - void (os); - - if (urlOptions.mode === 'print') { - return ''; - } - - var head = ''; - - if (typeof charts['system.swap'] !== 'undefined') { - head += '
'; - } - - if (typeof charts['system.io'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.pgpgio'] !== 'undefined') { - head += '
'; - - head += '
'; - } - - if (typeof charts['system.cpu'] !== 'undefined') { - head += '
'; - } - - if (typeof charts['system.net'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ip'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ipv4'] !== 'undefined') { - head += '
'; - - head += '
'; - } - else if (typeof charts['system.ipv6'] !== 'undefined') { - head += '
'; - - head += '
'; - } - - if (typeof charts['system.ram'] !== 'undefined') { - head += '
'; - } - - return head; -} - -function generateHeadCharts(type, chart, duration) { - if (urlOptions.mode === 'print') { - return ''; - } - - 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](netdataDashboard.os, chart.id).replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id); - } else { - head += hcharts[hi].replace(/CHART_DURATION/g, duration.toString()).replace(/CHART_UNIQUE_ID/g, chart.id); - } - hi++; - } - } - 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; - options.duration = duration; - options.update_every = data.update_every; - - var html = ''; - var sidebar = ''; - div.innerHTML = html; - document.getElementById('sidebar').innerHTML = sidebar; - - if (urlOptions.highlight === true) { - NETDATA.globalChartUnderlay.init(null - , urlOptions.highlight_after - , urlOptions.highlight_before - , (urlOptions.after > 0) ? urlOptions.after : null - , (urlOptions.before > 0) ? urlOptions.before : null - ); - } else { - NETDATA.globalChartUnderlay.clear(); - } - - if (urlOptions.mode === 'print') { - printPage(); - } else { - finalizePage(); - } -} - -function renderChartsAndMenu(data) { - options.menus = {}; - options.submenu_names = {}; - - var menus = options.menus; - var charts = data.charts; - var m, menu_key; - - for (var c in charts) { - if (!charts.hasOwnProperty(c)) { - continue; - } - - var chart = charts[c]; - enrichChartData(chart); - m = chart.menu; - - // create the menu - if (typeof menus[m] === 'undefined') { - menus[m] = { - menu_pattern: chart.menu_pattern, - priority: chart.priority, - submenus: {}, - title: netdataDashboard.menuTitle(chart), - icon: netdataDashboard.menuIcon(chart), - info: netdataDashboard.menuInfo(chart), - height: netdataDashboard.menuHeight(chart) * options.chartsHeight - }; - } else { - if (typeof (menus[m].menu_pattern) === 'undefined') { - menus[m].menu_pattern = chart.menu_pattern; - } - - if (chart.priority < menus[m].priority) { - menus[m].priority = chart.priority; - } - } - - menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m; - - // create the submenu - if (typeof menus[m].submenus[chart.submenu] === 'undefined') { - menus[m].submenus[chart.submenu] = { - priority: chart.priority, - charts: [], - title: null, - info: netdataDashboard.submenuInfo(menu_key, chart.submenu), - height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height) - }; - } else { - if (chart.priority < menus[m].submenus[chart.submenu].priority) { - menus[m].submenus[chart.submenu].priority = chart.priority; - } - } - - // index the chart in the menu/submenu - menus[m].submenus[chart.submenu].charts.push(chart); - } - - // propagate the descriptive subname given to QoS - // to all the other submenus with the same name - for (var m in menus) { - if (!menus.hasOwnProperty(m)) { - continue; - } - - for (var s in menus[m].submenus) { - if (!menus[m].submenus.hasOwnProperty(s)) { - continue; - } - - // set the family using a name - if (typeof options.submenu_names[s] !== 'undefined') { - menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; - } else { - menu_key = (typeof (menus[m].menu_pattern) !== 'undefined') ? menus[m].menu_pattern : m; - menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s); - } - } - } - - renderPage(menus, data); -} - -// ---------------------------------------------------------------------------- - -function loadJs(url, callback) { - $.ajax({ - url: url.startsWith("http") ? url : transformWithOldSuffix(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(); - } - }) -} - -var clipboardLoaded = false; - -function loadClipboard(callback) { - if (clipboardLoaded === false) { - clipboardLoaded = true; - loadJs('lib/clipboard-polyfill-be05dad.js', callback); - } else { - callback(); - } -} - -var bootstrapTableLoaded = false; - -function loadBootstrapTable(callback) { - if (bootstrapTableLoaded === false) { - bootstrapTableLoaded = true; - loadJs('lib/bootstrap-table-1.11.0.min.js', function () { - loadJs('lib/bootstrap-table-export-1.11.0.min.js', function () { - loadJs('lib/tableExport-1.6.0.min.js', callback); - }) - }); - } else { - callback(); - } -} - -var bootstrapSliderLoaded = false; - -function loadBootstrapSlider(callback) { - if (bootstrapSliderLoaded === false) { - bootstrapSliderLoaded = true; - loadJs('lib/bootstrap-slider-10.0.0.min.js', function () { - NETDATA._loadCSS(transformWithOldSuffix("css/bootstrap-slider-10.0.0.min.css")); - callback(); - }); - } else { - callback(); - } -} - -var lzStringLoaded = false; - -function loadLzString(callback) { - if (lzStringLoaded === false) { - lzStringLoaded = true; - loadJs('lib/lz-string-1.4.4.min.js', callback); - } else { - callback(); - } -} - -var pakoLoaded = false; - -function loadPako(callback) { - if (pakoLoaded === false) { - pakoLoaded = true; - loadJs('lib/pako-1.0.6.min.js', callback); - } else { - callback(); - } -} - -// ---------------------------------------------------------------------------- - -function clipboardCopy(text) { - clipboard.writeText(text); -} - -function clipboardCopyBadgeEmbed(url) { - clipboard.writeText(''); -} - -// ---------------------------------------------------------------------------- - -function alarmsUpdateModal() { - var active = '

触发警报

'; - var all = '

所有作用中的警报

'; - var footer = '
netdata badges 会自动重新整理。不同颜色分表代表的警报状态:  红色  表示重大,  橘色  表示警告,  绿色  表示良好,  灰色  表示未定义 (例如无资料或无状态),  黑色  表示尚未初始化。您可以复制这里的网址并将它们嵌入到任一个网页。
netdata 能够发送这些警报通知。请参阅 这个设定档 了解更多资讯。'; - - loadClipboard(function () { - }); - - NETDATA.alarms.get('all', function (data) { - options.alarm_families = []; - - alarmsCallback(data); - - 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 alarmid4human(id) { - if (id === 0) { - return '-'; - } - - return id.toString(); - } - - function timestamp4human(timestamp, space) { - if (timestamp === 0) { - return '-'; - } - - if (typeof space === 'undefined') { - space = ' '; - } - - var t = new Date(timestamp * 1000); - var now = new Date(); - - if (t.toDateString() === now.toDateString()) { - return t.toLocaleTimeString(); - } - - return t.toLocaleDateString() + space + t.toLocaleTimeString(); - } - - function alarm_lookup_explain(alarm, chart) { - var dimensions = ' of all values '; - - if (chart.dimensions.length > 1) { - dimensions = ' of the sum of all dimensions '; - } - - if (typeof alarm.lookup_dimensions !== 'undefined') { - var d = alarm.lookup_dimensions.replace(/|/g, ','); - var x = d.split(','); - if (x.length > 1) { - dimensions = 'of the sum of dimensions ' + alarm.lookup_dimensions + ' '; - } else { - dimensions = 'of all values of dimension ' + alarm.lookup_dimensions + ' '; - } - } - - return '' + alarm.lookup_method + ' ' - + dimensions + ', of chart ' + alarm.chart + '' - + ', starting ' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, { space: ' ' }) + ' and up to ' + NETDATA.seconds4human(alarm.lookup_before, { space: ' ' }) + '' - + ((alarm.lookup_options) ? (', with options ' + alarm.lookup_options.replace(/ /g, ', ') + '') : '') - + '.'; - } - - function alarm_to_html(alarm, full) { - var chart = options.data.charts[alarm.chart]; - if (typeof (chart) === 'undefined') { - chart = options.data.charts_by_name[alarm.chart]; - if (typeof (chart) === 'undefined') { - // this means the charts loaded are incomplete - // probably netdata was restarted and more alarms - // are now available. - console.log('Cannot find chart ' + alarm.chart + ', you probably need to refresh the page.'); - return ''; - } - } - - var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined'); - var badge_url = NETDATA.alarms.server + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto'; - - var action_buttons = '
 
role: ' + alarm.recipient + '
 
' - + '
' - + '
' - + '
'; - - var html = '
' - + ''; - - return html; - } - - function alarm_family_show(id) { - var html = '
' + alarm.chart + '
 

 
' + alarm.info + '' + action_buttons + '
' - + ((typeof alarm.warn !== 'undefined') ? ('') : '') - + ((typeof alarm.crit !== 'undefined') ? ('') : ''); - - if (full === true) { - var units = chart.units; - if (units === '%') { - units = '%'; - } - - html += ((typeof alarm.lookup_after !== 'undefined') ? ('') : '') - + ((typeof alarm.calc !== 'undefined') ? ('') : '') - + ((chart.green !== null) ? ('') : '') - + ((chart.red !== null) ? ('') : ''); - } - - if (alarm.warn_repeat_every > 0) { - html += ''; - } - - if (alarm.crit_repeat_every > 0) { - html += ''; - } - - 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 += '
hysteresis ' + NETDATA.seconds4human(alarm.delay_up_duration, { - space: ' ', - negative_suffix: '' - }); - } else { - delay = '
hysteresis '; - if (alarm.delay_up_duration > 0) { - delay += 'on escalation ' + NETDATA.seconds4human(alarm.delay_up_duration, { - space: ' ', - negative_suffix: '' - }) + ', '; - } - if (alarm.delay_down_duration > 0) { - delay += 'on recovery ' + NETDATA.seconds4human(alarm.delay_down_duration, { - space: ' ', - negative_suffix: '' - }) + ', '; - } - } - if (alarm.delay_multiplier !== 1.0) { - delay += 'multiplied by ' + alarm.delay_multiplier.toString() + ''; - delay += ', up to ' + NETDATA.seconds4human(alarm.delay_max_duration, { - space: ' ', - negative_suffix: '' - }) + ''; - } - delay += '
'; - } - - html += '
' - + ((has_alarm === true) ? ('') : '') - + '' - + '
warning when' + alarm.warn + '
critical when' + alarm.crit + '
db lookup' + alarm_lookup_explain(alarm, chart) + '
calculation' + alarm.calc + '
green threshold' + chart.green + ' ' + units + '
red threshold' + chart.red + ' ' + units + '
repeat warning' + NETDATA.seconds4human(alarm.warn_repeat_every) + '
repeat critical' + NETDATA.seconds4human(alarm.crit_repeat_every) + '
check every' + NETDATA.seconds4human(alarm.update_every, { - space: ' ', - negative_suffix: '' - }) + '
execute' + alarm.exec + '' + delay + '
source' + alarm.source + '
'; - 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 += '
'; - - $('#alarm_all_' + id.toString()).html(html); - enableTooltipsAndPopovers(); - } - - // find the proper family of each alarm - var x, family, alarm; - var count_active = 0; - var count_all = 0; - var families = {}; - var families_sort = []; - for (x in data.alarms) { - if (!data.alarms.hasOwnProperty(x)) { - continue; - } - - alarm = data.alarms[x]; - 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: [], - priority: chart.priority - }; - - families_sort.push(families[family]); - } - - if (chart.priority < families[family].priority) { - families[family].priority = chart.priority; - } - - 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 naturalSortCompare(a.name, b.name); - }); - - var i = 0; - var fc = 0; - var len = families_sorted.length; - while (len--) { - family = families_sorted[i++].name; - var active_family_added = false; - var expanded = 'true'; - var collapsed = ''; - var cin = 'in'; - - if (fc !== 0) { - all += ""; - expanded = 'false'; - collapsed = 'class="collapsed"'; - cin = ''; - } - - all += '
'; - - options.alarm_families[fc] = families[family]; - - fc++; - - var arr = families[family].arr; - var c = arr.length; - while (c--) { - alarm = arr[c]; - if (alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { - if (!active_family_added) { - active_family_added = true; - active += '

' + family + '

'; - } - count_active++; - active += alarm_to_html(alarm, true); - } - - count_all++; - } - } - active += ""; - if (families_sorted.length > 0) { - all += "
"; - } - all += ""; - - if (!count_active) { - active += '

一切正常。没有警报。
'; - } else { - active += footer; - } - - if (!count_all) { - all += "

此系统中没有运行警报。

"; - } else { - all += footer; - } - - document.getElementById('alarms_active').innerHTML = active; - document.getElementById('alarms_all').innerHTML = all; - enableTooltipsAndPopovers(); - - if (families_sorted.length > 0) { - alarm_family_show(0); - } - - // register bootstrap events - var $accordion = $('#alarms_all_accordion'); - $accordion.on('show.bs.collapse', function (d) { - var target = $(d.target); - var id = $(target).data('alarm-id'); - alarm_family_show(id); - }); - $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 = '

警报记录

'; - - 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' - }, - onClickRow: function (row, $element,field) { - void (field); - void ($element); - let main_url; - let common_url = "&host=" + encodeURIComponent(row['hostname']) + "&chart=" + encodeURIComponent(row['chart']) + "&family=" + encodeURIComponent(row['family']) + "&alarm=" + encodeURIComponent(row['name']) + "&alarm_unique_id=" + row['unique_id'] + "&alarm_id=" + row['alarm_id'] + "&alarm_event_id=" + row['alarm_event_id'] + "&alarm_when=" + row['when']; - if (NETDATA.registry.isUsingGlobalRegistry() && NETDATA.registry.machine_guid != null) { - main_url = "https://netdata.cloud/alarms/redirect?agentID=" + NETDATA.registry.machine_guid + common_url; - } else { - main_url = NETDATA.registry.server + "/goto-host-from-alarm.html?" + common_url ; - } - window.open(main_url,"_blank"); - }, - rowStyle: function (row, index) { - void (index); - - 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: '事件日期', - valign: 'middle', - titleTooltip: 'The date and time the even took place', - formatter: function (value, row, index) { - void (row); - void (index); - return timestamp4human(value, ' '); - }, - align: 'center', - switchable: false, - sortable: true - }, - { - field: 'hostname', - title: '主机', - valign: 'middle', - titleTooltip: 'The host that generated this event', - align: 'center', - visible: false, - sortable: true - }, - { - field: 'unique_id', - title: '唯一 ID', - titleTooltip: 'The host unique ID for this event', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_id', - title: '警报 ID', - titleTooltip: 'The ID of the alarm that generated this event', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'alarm_event_id', - title: '警报事件 ID', - titleTooltip: 'The incremental ID of this event for the given alarm', - formatter: function (value, row, index) { - void (row); - void (index); - return alarmid4human(value); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'chart', - title: '图表', - 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: '警报', - titleTooltip: 'The alarm name that generated this event', - formatter: function (value, row, index) { - void (row); - void (index); - return value.toString().replace(/_/g, ' '); - }, - align: 'center', - valign: 'middle', - switchable: false, - sortable: true - }, - { - field: 'value_string', - title: 'Friendly Value', - titleTooltip: 'The value of the alarm, that triggered this event', - align: 'right', - valign: 'middle', - sortable: true - }, - { - field: 'old_value_string', - title: 'Friendly Old Value', - titleTooltip: 'The value of the alarm, just before this event', - align: 'right', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'old_value', - title: 'Old Value', - titleTooltip: 'The value of the alarm, just before this event', - formatter: function (value, row, index) { - void (row); - void (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) { - void (row); - void (index); - return ((value !== null) ? Math.round(value * 100) / 100 : 'NaN').toString(); - }, - align: 'right', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'units', - title: '单位', - titleTooltip: 'The units of the value of the alarm', - align: 'left', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'old_status', - title: '先前状态', - titleTooltip: 'The status of the alarm, just before this event', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'status', - title: '状态', - 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) { - void (row); - void (index); - return NETDATA.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) { - void (row); - void (index); - return NETDATA.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) { - void (row); - void (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) { - void (row); - void (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) { - void (row); - void (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) { - void (row); - void (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) { - void (row); - void (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) { - void (row); - void (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) { - void (row); - void (index); - - return NETDATA.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) { - void (row); - void (index); - return timestamp4human(value, ' '); - }, - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'info', - title: '说明', - titleTooltip: 'A short description of the alarm', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - }, - { - field: 'source', - title: '警报来源', - titleTooltip: 'The source of configuration of the alarm', - align: 'center', - valign: 'middle', - visible: false, - sortable: true - } - ] - }); - // console.log($('#alarms_log_table').bootstrapTable('getOptions')); - }); - }); -} - -function alarmsCallback(data) { - var count = 0, x; - for (x in data.alarms) { - if (!data.alarms.hasOwnProperty(x)) { - continue; - } - - 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 = ''; - } -} - -function initializeDynamicDashboardWithData(data) { - if (data !== null) { - options.hostname = data.hostname; - options.data = data; - options.version = data.version; - options.release_channel = data.release_channel; - netdataDashboard.os = data.os; - - if (typeof data.hosts !== 'undefined') { - options.hosts = data.hosts; - } - - // update the dashboard hostname - document.getElementById('hostname').innerHTML = '' + options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString() + '  '; - document.getElementById('hostname').href = NETDATA.serverDefault; - document.getElementById('netdataVersion').innerHTML = options.version; - - if (netdataSnapshotData !== null) { - $('#alarmsButton').hide(); - $('#updateButton').hide(); - // $('#loadButton').hide(); - $('#saveButton').hide(); - $('#printButton').hide(); - } - - // update the dashboard title - document.title = options.hostname + ' netdata 仪表板'; - - // 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) { - if (!charts.hasOwnProperty(x)) { - continue; - } - - var chart = charts[x]; - data.charts_by_name[chart.name] = chart; - } - - // render all charts - renderChartsAndMenu(data); - - // Ensure MyNetdata menu is rendered with latest host info #5370 - renderMyNetdataMenu(isSignedIn() ? cloudAgents : registryAgents); - } -} - -// an object to keep initialization configuration -// needed due to the async nature of the XSS modal -var initializeConfig = { - url: null, - custom_info: true, -}; - -function loadCustomDashboardInfo(url, callback) { - loadJs(url, function () { - $.extend(true, netdataDashboard, customDashboard); - callback(); - }); -} - -function initializeChartsAndCustomInfo() { - NETDATA.alarms.callback = alarmsCallback; - - // download all the charts the server knows - NETDATA.chartRegistry.downloadAll(initializeConfig.url, function (data) { - if (data !== null) { - if (initializeConfig.custom_info === true && typeof data.custom_info !== 'undefined' && data.custom_info !== "" && netdataSnapshotData === null) { - //console.log('loading custom dashboard decorations from server ' + initializeConfig.url); - loadCustomDashboardInfo(NETDATA.serverDefault + data.custom_info, function () { - initializeDynamicDashboardWithData(data); - }); - } else { - //console.log('not loading custom dashboard decorations from server ' + initializeConfig.url); - initializeDynamicDashboardWithData(data); - } - } - }); -} - -function xssModalDisableXss() { - //console.log('disabling xss checks'); - NETDATA.xss.enabled = false; - NETDATA.xss.enabled_for_data = false; - initializeConfig.custom_info = true; - initializeChartsAndCustomInfo(); - return false; -} - -function xssModalKeepXss() { - //console.log('keeping xss checks'); - NETDATA.xss.enabled = true; - NETDATA.xss.enabled_for_data = true; - initializeConfig.custom_info = false; - initializeChartsAndCustomInfo(); - return false; -} - -function initializeDynamicDashboard(netdata_url) { - if (typeof netdata_url === 'undefined' || netdata_url === null) { - netdata_url = NETDATA.serverDefault; - } - - initializeConfig.url = netdata_url; - - // initialize clickable alarms - NETDATA.alarms.chart_div_offset = -50; - NETDATA.alarms.chart_div_id_prefix = 'chart_'; - NETDATA.alarms.chart_div_animation_duration = 0; - - NETDATA.pause(function () { - if (typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true) { - //$("#loadOverlay").css("display","none"); - document.getElementById('netdataXssModalServer').innerText = initializeConfig.url; - $('#xssModal').modal('show'); - } else { - initializeChartsAndCustomInfo(); - } - }); -} - -// ---------------------------------------------------------------------------- - -function versionLog(msg) { - document.getElementById('versionCheckLog').innerHTML = msg; -} - -// New way of checking for updates, based only on versions - -function versionsMatch(v1, v2) { - if (v1 == v2) { - return true; - } else { - let s1 = v1.split('.'); - let s2 = v2.split('.'); - // Check major version - let n1 = parseInt(s1[0].substring(1, 2), 10); - let n2 = parseInt(s2[0].substring(1, 2), 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - // Check minor version - n1 = parseInt(s1[1], 10); - n2 = parseInt(s2[1], 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - // Split patch: format could be e.g. 0-22-nightly - s1 = s1[2].split('-'); - s2 = s2[2].split('-'); - - n1 = parseInt(s1[0], 10); - n2 = parseInt(s2[0], 10); - if (n1 < n2) return false; - else if (n1 > n2) return true; - - n1 = (s1.length > 1) ? parseInt(s1[1], 10) : 0; - n2 = (s2.length > 1) ? parseInt(s2[1], 10) : 0; - if (n1 < n2) return false; - else return true; - } -} - -function getGithubLatestVersion(callback) { - versionLog('正在从 github 下载最新版本 ID...'); - - $.ajax({ - url: 'https://api.github.com/repos/netdata/netdata/releases/latest', - async: true, - cache: false - }) - .done(function (data) { - data = data.tag_name.replace(/(\r\n|\n|\r| |\t)/gm, ""); - versionLog('从 github 取得最新版本是 ' + data); - callback(data); - }) - .fail(function () { - versionLog('从 github 下载最新版本 ID 失败!'); - callback(null); - }); -} - -function getGCSLatestVersion(callback) { - versionLog('Downloading latest version id from GCS...'); - $.ajax({ - url: "https://www.googleapis.com/storage/v1/b/netdata-nightlies/o/latest-version.txt", - async: true, - cache: false - }) - .done(function (response) { - $.ajax({ - url: response.mediaLink, - async: true, - cache: false - }) - .done(function (data) { - data = data.replace(/(\r\n|\n|\r| |\t)/gm, ""); - versionLog('Latest nightly version from GCS is ' + data); - callback(data); - }) - .fail(function (xhr, textStatus, errorThrown) { - versionLog('Failed to download the latest nightly version id from GCS!'); - callback(null); - }); - }) - .fail(function (xhr, textStatus, errorThrown) { - versionLog('Failed to download the latest nightly version from GCS!'); - callback(null); - }); -} - - -function checkForUpdateByVersion(force, callback) { - if (options.release_channel === 'stable') { - getGithubLatestVersion(function (sha2) { - callback(options.version, sha2); - }); - } else { - getGCSLatestVersion(function (sha2) { - callback(options.version, sha2); - }); - } - return null; -} - -function notifyForUpdate(force) { - versionLog('

正在检查更新...

'); - - var now = Date.now(); - - if (typeof force === 'undefined' || force !== true) { - var last = loadLocalStorage('last_update_check'); - - if (typeof last === 'string') { - last = parseInt(last); - } else { - last = 0; - } - - if (now - last < 3600000 * 8) { - // no need to check it - too soon - return; - } - } - - checkForUpdateByVersion(force, function (sha1, sha2) { - var save = false; - - if (sha1 === null) { - save = false; - versionLog('

取得您的 netdata 版本失败!

You can always get the latest netdata from its github page.

'); - } else if (sha2 === null) { - save = false; - versionLog('

从 github 取得 netdata 最新版本失败。

您也可以从 its github page 取得最新 netdata 版本。

'); - } else if (versionsMatch(sha1, sha2)) { - save = true; - versionLog('

您已经是最新版本的 netdata!

还没有更新?
或许,我们还需要一些动力继续前进!

如果您还没有做好更新的准备,请您 到 github 给 netdata at its github page.

'); - } else { - save = true; - var compare = 'https://learn.netdata.cloud/docs/agent/changelog/'; - versionLog('

New version of netdata available!

Latest version: ' + sha2 + '

Click here for the changes log and
click here for directions on updating your netdata installation.

We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
Keeping your netdata updated is generally a good idea.

'); - - document.getElementById('update_badge').innerHTML = '!'; - } - - if (save) { - saveLocalStorage('last_update_check', now.toString()); - } - }); -} - -// ---------------------------------------------------------------------------- -// printing dashboards - -function showPageFooter() { - document.getElementById('footer').style.display = 'block'; -} - -function printPreflight() { - var url = document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString() + urlOptions.genHash() + ';mode=print'; - var width = 990; - var height = screen.height * 90 / 100; - //console.log(url); - //console.log(document.location); - window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes'); - $('#printPreflightModal').modal('hide'); -} - -function printPage() { - var print_is_rendering = true; - - $('#printModal').on('hide.bs.modal', function (e) { - if (print_is_rendering === true) { - e.preventDefault(); - return false; - } - - return true; - }); - - $('#printModal').on('show.bs.modal', function () { - var print_options = { - stop_updates_when_focus_is_lost: false, - update_only_visible: false, - sync_selection: false, - eliminate_zero_dimensions: false, - pan_and_zoom_data_padding: false, - show_help: false, - legend_toolbox: false, - resize_charts: false, - pixels_per_point: 1 - }; - - var x; - for (x in print_options) { - if (print_options.hasOwnProperty(x)) { - NETDATA.options.current[x] = print_options[x]; - } - } - - NETDATA.parseDom(); - showPageFooter(); - - NETDATA.globalSelectionSync.stop(); - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); - // NETDATA.onresize(); - - var el = document.getElementById('printModalProgressBar'); - var eltxt = document.getElementById('printModalProgressBarText'); - - function update_chart(idx) { - var state = NETDATA.options.targets[--idx]; - - var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length; - $(el).css('width', pcent + '%').attr('aria-valuenow', pcent); - eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id; - - setTimeout(function () { - state.updateChart(function () { - NETDATA.options.targets[idx].resizeForPrint(); - - if (idx > 0) { - update_chart(idx); - } else { - print_is_rendering = false; - $('#printModal').modal('hide'); - window.print(); - window.close(); - } - }) - }, 0); - } - - print_is_rendering = true; - update_chart(NETDATA.options.targets.length); - }); - - $('#printModal').modal('show'); -} - -// -------------------------------------------------------------------- - -function jsonStringifyFn(obj) { - return JSON.stringify(obj, function (key, value) { - return (typeof value === 'function') ? value.toString() : value; - }); -} - -function jsonParseFn(str) { - return JSON.parse(str, function (key, value) { - if (typeof value != 'string') { - return value; - } - return (value.substring(0, 8) == 'function') ? eval('(' + value + ')') : value; - }); -} - -// -------------------------------------------------------------------- - -var snapshotOptions = { - bytes_per_chart: 2048, - compressionDefault: 'pako.deflate.base64', - - compressions: { - 'none': { - bytes_per_point_memory: 5.2, - bytes_per_point_disk: 5.6, - - compress: function (s) { - return s; - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return s; - } - }, - - 'pako.deflate.base64': { - bytes_per_point_memory: 1.8, - bytes_per_point_disk: 1.9, - - compress: function (s) { - return btoa(pako.deflate(s, { to: 'string' })); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return pako.inflate(atob(s), { to: 'string' }); - } - }, - - 'pako.deflate': { - bytes_per_point_memory: 1.4, - bytes_per_point_disk: 3.2, - - compress: function (s) { - return pako.deflate(s, { to: 'string' }); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return pako.inflate(s, { to: 'string' }); - } - }, - - 'lzstring.utf16': { - bytes_per_point_memory: 1.7, - bytes_per_point_disk: 2.6, - - compress: function (s) { - return LZString.compressToUTF16(s); - }, - - compressed_length: function (s) { - return s.length * 2; - }, - - uncompress: function (s) { - return LZString.decompressFromUTF16(s); - } - }, - - 'lzstring.base64': { - bytes_per_point_memory: 2.1, - bytes_per_point_disk: 2.3, - - compress: function (s) { - return LZString.compressToBase64(s); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return LZString.decompressFromBase64(s); - } - }, - - 'lzstring.uri': { - bytes_per_point_memory: 2.1, - bytes_per_point_disk: 2.3, - - compress: function (s) { - return LZString.compressToEncodedURIComponent(s); - }, - - compressed_length: function (s) { - return s.length; - }, - - uncompress: function (s) { - return LZString.decompressFromEncodedURIComponent(s); - } - } - } -}; - -// -------------------------------------------------------------------- -// loading snapshots - -function loadSnapshotModalLog(priority, msg) { - document.getElementById('loadSnapshotStatus').className = "alert alert-" + priority; - document.getElementById('loadSnapshotStatus').innerHTML = msg; -} - -var tmpSnapshotData = null; - -function loadSnapshot() { - $('#loadSnapshotImport').addClass('disabled'); - - if (tmpSnapshotData === null) { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'no data have been loaded'); - return; - } - - loadPako(function () { - loadLzString(function () { - loadSnapshotModalLog('info', 'Please wait, activating snapshot...'); - $('#loadSnapshotModal').modal('hide'); - - netdataShowAlarms = false; - netdataRegistry = false; - netdataServer = tmpSnapshotData.server; - NETDATA.serverDefault = netdataServer; - - document.getElementById('charts_div').innerHTML = ''; - document.getElementById('sidebar').innerHTML = ''; - NETDATA.globalReset(); - - if (typeof tmpSnapshotData.hash !== 'undefined') { - urlOptions.hash = tmpSnapshotData.hash; - } else { - urlOptions.hash = '#'; - } - - if (typeof tmpSnapshotData.info !== 'undefined') { - var info = jsonParseFn(tmpSnapshotData.info); - if (typeof info.menu !== 'undefined') { - netdataDashboard.menu = info.menu; - } - - if (typeof info.submenu !== 'undefined') { - netdataDashboard.submenu = info.submenu; - } - - if (typeof info.context !== 'undefined') { - netdataDashboard.context = info.context; - } - } - - if (typeof tmpSnapshotData.compression !== 'string') { - tmpSnapshotData.compression = 'none'; - } - - if (typeof snapshotOptions.compressions[tmpSnapshotData.compression] === 'undefined') { - alert('unknown compression method: ' + tmpSnapshotData.compression); - tmpSnapshotData.compression = 'none'; - } - - tmpSnapshotData.uncompress = snapshotOptions.compressions[tmpSnapshotData.compression].uncompress; - netdataSnapshotData = tmpSnapshotData; - - urlOptions.after = tmpSnapshotData.after_ms; - urlOptions.before = tmpSnapshotData.before_ms; - - if (typeof tmpSnapshotData.highlight_after_ms !== 'undefined' - && tmpSnapshotData.highlight_after_ms !== null - && tmpSnapshotData.highlight_after_ms > 0 - && typeof tmpSnapshotData.highlight_before_ms !== 'undefined' - && tmpSnapshotData.highlight_before_ms !== null - && tmpSnapshotData.highlight_before_ms > 0 - ) { - urlOptions.highlight_after = tmpSnapshotData.highlight_after_ms; - urlOptions.highlight_before = tmpSnapshotData.highlight_before_ms; - urlOptions.highlight = true; - } else { - urlOptions.highlight_after = 0; - urlOptions.highlight_before = 0; - urlOptions.highlight = false; - } - - netdataCheckXSS = false; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded - NETDATA.xss.enabled = true; // we should not do any remote requests, but if we do, check them - NETDATA.xss.enabled_for_data = true; // check also snapshot data - that have been excluded from the initial check, due to compression - loadSnapshotPreflightEmpty(); - initializeDynamicDashboard(); - }); - }); -}; - -function loadSnapshotPreflightFile(file) { - var filename = NETDATA.xss.string(file.name); - var fr = new FileReader(); - fr.onload = function (e) { - document.getElementById('loadSnapshotFilename').innerHTML = filename; - var result = null; - try { - result = NETDATA.xss.checkAlways('snapshot', JSON.parse(e.target.result), /^(snapshot\.info|snapshot\.data)$/); - - //console.log(result); - var date_after = new Date(result.after_ms); - var date_before = new Date(result.before_ms); - - if (typeof result.charts_ok === 'undefined') { - result.charts_ok = 'unknown'; - } - - if (typeof result.charts_failed === 'undefined') { - result.charts_failed = 0; - } - - if (typeof result.compression === 'undefined') { - result.compression = 'none'; - } - - if (typeof result.data_size === 'undefined') { - result.data_size = 0; - } - - document.getElementById('loadSnapshotFilename').innerHTML = '' + filename + ''; - document.getElementById('loadSnapshotHostname').innerHTML = '' + result.hostname + ', netdata version: ' + result.netdata_version.toString() + ''; - document.getElementById('loadSnapshotURL').innerHTML = result.url; - document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point'; - document.getElementById('loadSnapshotInfo').innerHTML = 'version: ' + result.snapshot_version.toString() + ', includes ' + result.charts_ok.toString() + ' unique chart data queries ' + ((result.charts_failed > 0) ? ('' + result.charts_failed.toString() + ' failed') : '').toString() + ', compressed with ' + result.compression.toString() + ', data size ' + (Math.round(result.data_size * 100 / 1024 / 1024) / 100).toString() + ' MB'; - document.getElementById('loadSnapshotTimeRange').innerHTML = '' + NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + ' to ' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before) + ''; - document.getElementById('loadSnapshotComments').innerHTML = ((result.comments) ? result.comments : '').toString(); - loadSnapshotModalLog('success', 'File loaded, click Import to render it!'); - $('#loadSnapshotImport').removeClass('disabled'); - - tmpSnapshotData = result; - } - catch (e) { - console.log(e); - document.getElementById('loadSnapshotStatus').className = "alert alert-danger"; - document.getElementById('loadSnapshotStatus').innerHTML = "Failed to parse this file!"; - $('#loadSnapshotImport').addClass('disabled'); - } - } - - //console.log(file); - fr.readAsText(file); -}; - -function loadSnapshotPreflightEmpty() { - document.getElementById('loadSnapshotFilename').innerHTML = ''; - document.getElementById('loadSnapshotHostname').innerHTML = ''; - document.getElementById('loadSnapshotURL').innerHTML = ''; - document.getElementById('loadSnapshotCharts').innerHTML = ''; - document.getElementById('loadSnapshotInfo').innerHTML = ''; - document.getElementById('loadSnapshotTimeRange').innerHTML = ''; - document.getElementById('loadSnapshotComments').innerHTML = ''; - loadSnapshotModalLog('success', 'Browse for a snapshot file (or drag it and drop it here), then click Import to render it.'); - $('#loadSnapshotImport').addClass('disabled'); -}; - -var loadSnapshotDragAndDropInitialized = false; - -function loadSnapshotDragAndDropSetup() { - if (loadSnapshotDragAndDropInitialized === false) { - loadSnapshotDragAndDropInitialized = true; - $('#loadSnapshotDragAndDrop') - .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) { - e.preventDefault(); - e.stopPropagation(); - }) - .on('drop', function (e) { - if (e.originalEvent.dataTransfer.files.length) { - loadSnapshotPreflightFile(e.originalEvent.dataTransfer.files.item(0)); - } else { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'No file selected'); - } - }); - } -}; - -function loadSnapshotPreflight() { - var files = document.getElementById('loadSnapshotSelectFiles').files; - if (files.length <= 0) { - loadSnapshotPreflightEmpty(); - loadSnapshotModalLog('danger', 'No file selected'); - return; - } - - loadSnapshotModalLog('info', 'Loading file...'); - - loadSnapshotPreflightFile(files.item(0)); -} - -// -------------------------------------------------------------------- -// saving snapshots - -var saveSnapshotStop = false; - -function saveSnapshotCancel() { - saveSnapshotStop = true; -} - -var saveSnapshotModalInitialized = false; - -function saveSnapshotModalSetup() { - if (saveSnapshotModalInitialized === false) { - saveSnapshotModalInitialized = true; - $('#saveSnapshotModal') - .on('hide.bs.modal', saveSnapshotCancel) - .on('show.bs.modal', saveSnapshotModalInit) - .on('shown.bs.modal', function () { - $('#saveSnapshotResolutionSlider').find(".slider-handle:first").attr("tabindex", 1); - document.getElementById('saveSnapshotComments').focus(); - }); - } -}; - -function saveSnapshotModalLog(priority, msg) { - document.getElementById('saveSnapshotStatus').className = "alert alert-" + priority; - document.getElementById('saveSnapshotStatus').innerHTML = msg; -} - -function saveSnapshotModalShowExpectedSize() { - var points = Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint); - var priority = 'info'; - var msg = 'A moderate snapshot.'; - - var sizemb = Math.round( - (options.data.charts_count * snapshotOptions.bytes_per_chart - + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_disk) - * 10 / 1024 / 1024) / 10; - - var memmb = Math.round( - (options.data.charts_count * snapshotOptions.bytes_per_chart - + options.data.dimensions_count * points * snapshotOptions.compressions[saveSnapshotCompression].bytes_per_point_memory) - * 10 / 1024 / 1024) / 10; - - if (sizemb < 10) { - priority = 'success'; - msg = 'A nice small snapshot!'; - } - if (sizemb > 50) { - priority = 'warning'; - msg = 'Will stress your browser...'; - } - if (sizemb > 100) { - priority = 'danger'; - msg = 'Hm... good luck...'; - } - - saveSnapshotModalLog(priority, 'The snapshot will have ' + points.toString() + ' points per dimension. Expected size on disk ' + sizemb + ' MB, at browser memory ' + memmb + ' MB.
' + msg); -} - -var saveSnapshotCompression = snapshotOptions.compressionDefault; - -function saveSnapshotSetCompression(name) { - saveSnapshotCompression = name; - document.getElementById('saveSnapshotCompressionName').innerHTML = saveSnapshotCompression; - saveSnapshotModalShowExpectedSize(); -} - -var saveSnapshotSlider = null; -var saveSnapshotSelectedSecondsPerPoint = 1; -var saveSnapshotViewDuration = 1; - -function saveSnapshotModalInit() { - $('#saveSnapshotModalProgressSection').hide(); - $('#saveSnapshotResolutionRadio').show(); - saveSnapshotModalLog('info', 'Select resolution and click Save'); - $('#saveSnapshotExport').removeClass('disabled'); - - loadBootstrapSlider(function () { - saveSnapshotViewDuration = options.duration; - var start_ms = Math.round(Date.now() - saveSnapshotViewDuration * 1000); - - if (NETDATA.globalPanAndZoom.isActive() === true) { - saveSnapshotViewDuration = Math.round((NETDATA.globalPanAndZoom.force_before_ms - NETDATA.globalPanAndZoom.force_after_ms) / 1000); - start_ms = NETDATA.globalPanAndZoom.force_after_ms; - } - - var start_date = new Date(start_ms); - var yyyymmddhhssmm = start_date.getFullYear() + NETDATA.zeropad(start_date.getMonth() + 1) + NETDATA.zeropad(start_date.getDate()) + '-' + NETDATA.zeropad(start_date.getHours()) + NETDATA.zeropad(start_date.getMinutes()) + NETDATA.zeropad(start_date.getSeconds()); - - document.getElementById('saveSnapshotFilename').value = 'netdata-' + options.hostname.toString() + '-' + yyyymmddhhssmm.toString() + '-' + saveSnapshotViewDuration.toString() + '.snapshot'; - saveSnapshotSetCompression(saveSnapshotCompression); - - var min = options.update_every; - var max = Math.round(saveSnapshotViewDuration / 100); - - if (NETDATA.globalPanAndZoom.isActive() === false) { - max = Math.round(saveSnapshotViewDuration / 50); - } - - var view = Math.round(saveSnapshotViewDuration / Math.round($(document.getElementById('charts_div')).width() / 2)); - - // console.log('view duration: ' + saveSnapshotViewDuration + ', min: ' + min + ', max: ' + max + ', view: ' + view); - - if (max < 10) { - max = 10; - } - if (max < min) { - max = min; - } - if (view < min) { - view = min; - } - if (view > max) { - view = max; - } - - if (saveSnapshotSlider !== null) { - saveSnapshotSlider.destroy(); - } - - saveSnapshotSlider = new Slider('#saveSnapshotResolutionSlider', { - ticks: [min, view, max], - min: min, - max: max, - step: options.update_every, - value: view, - scale: (max > 100) ? 'logarithmic' : 'linear', - tooltip: 'always', - formatter: function (value) { - if (value < 1) { - value = 1; - } - - if (value < options.data.update_every) { - value = options.data.update_every; - } - - saveSnapshotSelectedSecondsPerPoint = value; - saveSnapshotModalShowExpectedSize(); - - var seconds = ' seconds '; - if (value === 1) { - seconds = ' second '; - } - - return value + seconds + 'per point' + ((value === options.data.update_every) ? ', server default' : '').toString(); - } - }); - }); -} - -function saveSnapshot() { - loadPako(function () { - loadLzString(function () { - saveSnapshotStop = false; - $('#saveSnapshotModalProgressSection').show(); - $('#saveSnapshotResolutionRadio').hide(); - $('#saveSnapshotExport').addClass('disabled'); - - var filename = document.getElementById('saveSnapshotFilename').value; - // console.log(filename); - saveSnapshotModalLog('info', 'Generating snapshot as ' + filename.toString() + ''); - - var save_options = { - stop_updates_when_focus_is_lost: false, - update_only_visible: false, - sync_selection: false, - eliminate_zero_dimensions: true, - pan_and_zoom_data_padding: false, - show_help: false, - legend_toolbox: false, - resize_charts: false, - pixels_per_point: 1 - }; - var backedup_options = {}; - - var x; - for (x in save_options) { - if (save_options.hasOwnProperty(x)) { - backedup_options[x] = NETDATA.options.current[x]; - NETDATA.options.current[x] = save_options[x]; - } - } - - var el = document.getElementById('saveSnapshotModalProgressBar'); - var eltxt = document.getElementById('saveSnapshotModalProgressBarText'); - - options.data.charts_by_name = null; - - var saveData = { - hostname: options.hostname, - server: NETDATA.serverDefault, - netdata_version: options.data.version, - snapshot_version: 1, - after_ms: Date.now() - options.duration * 1000, - before_ms: Date.now(), - highlight_after_ms: urlOptions.highlight_after, - highlight_before_ms: urlOptions.highlight_before, - duration_ms: options.duration * 1000, - update_every_ms: options.update_every * 1000, - data_points: 0, - url: ((urlOptions.server !== null) ? urlOptions.server : document.location.origin.toString() + document.location.pathname.toString() + document.location.search.toString()).toString(), - comments: document.getElementById('saveSnapshotComments').value.toString(), - hash: urlOptions.hash, - charts: options.data, - info: jsonStringifyFn({ - menu: netdataDashboard.menu, - submenu: netdataDashboard.submenu, - context: netdataDashboard.context - }), - charts_ok: 0, - charts_failed: 0, - compression: saveSnapshotCompression, - data_size: 0, - data: {} - }; - - if (typeof snapshotOptions.compressions[saveData.compression] === 'undefined') { - alert('unknown compression method: ' + saveData.compression); - saveData.compression = 'none'; - } - - var compress = snapshotOptions.compressions[saveData.compression].compress; - var compressed_length = snapshotOptions.compressions[saveData.compression].compressed_length; - - function pack_api1_v1_chart_data(state) { - if (state.library_name === null || state.data === null) { - return; - } - - var data = state.data; - state.data = null; - data.state = null; - var str = JSON.stringify(data); - - if (typeof str === 'string') { - var cstr = compress(str); - saveData.data[state.chartDataUniqueID()] = cstr; - return compressed_length(cstr); - } else { - return 0; - } - } - - var clearPanAndZoom = false; - if (NETDATA.globalPanAndZoom.isActive() === false) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveData.after_ms, saveData.before_ms); - clearPanAndZoom = true; - } - - saveData.after_ms = NETDATA.globalPanAndZoom.force_after_ms; - saveData.before_ms = NETDATA.globalPanAndZoom.force_before_ms; - saveData.duration_ms = saveData.before_ms - saveData.after_ms; - saveData.data_points = Math.round((saveData.before_ms - saveData.after_ms) / (saveSnapshotSelectedSecondsPerPoint * 1000)); - saveSnapshotModalLog('info', 'Generating snapshot with ' + saveData.data_points.toString() + ' data points per dimension...'); - - var charts_count = 0; - var charts_ok = 0; - var charts_failed = 0; - - function saveSnapshotRestore() { - $('#saveSnapshotModal').modal('hide'); - - // restore the options - var x; - for (x in backedup_options) { - if (backedup_options.hasOwnProperty(x)) { - NETDATA.options.current[x] = backedup_options[x]; - } - } - - $(el).css('width', '0%').attr('aria-valuenow', 0); - eltxt.innerText = '0%'; - - if (clearPanAndZoom) { - NETDATA.globalPanAndZoom.clearMaster(); - } - - NETDATA.options.force_data_points = 0; - NETDATA.options.fake_chart_rendering = false; - NETDATA.onscroll_updater_enabled = true; - NETDATA.onresize(); - NETDATA.unpause(); - - $('#saveSnapshotExport').removeClass('disabled'); - } - - NETDATA.globalSelectionSync.stop(); - NETDATA.options.force_data_points = saveData.data_points; - NETDATA.options.fake_chart_rendering = true; - NETDATA.onscroll_updater_enabled = false; - NETDATA.abortAllRefreshes(); - - var size = 0; - var info = ' Resolution: ' + saveSnapshotSelectedSecondsPerPoint.toString() + ((saveSnapshotSelectedSecondsPerPoint === 1) ? ' second ' : ' seconds ').toString() + 'per point.'; - - function update_chart(idx) { - if (saveSnapshotStop === true) { - saveSnapshotModalLog('info', 'Cancelled!'); - saveSnapshotRestore(); - return; - } - - var state = NETDATA.options.targets[--idx]; - - var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length; - $(el).css('width', pcent + '%').attr('aria-valuenow', pcent); - eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id; - - setTimeout(function () { - charts_count++; - state.isVisible(true); - state.current.force_after_ms = saveData.after_ms; - state.current.force_before_ms = saveData.before_ms; - - state.updateChart(function (status, reason) { - state.current.force_after_ms = null; - state.current.force_before_ms = null; - - if (status === true) { - charts_ok++; - // state.log('ok'); - size += pack_api1_v1_chart_data(state); - } else { - charts_failed++; - state.log('failed to be updated: ' + reason); - } - - saveSnapshotModalLog((charts_failed) ? 'danger' : 'info', 'Generated snapshot data size ' + (Math.round(size * 100 / 1024 / 1024) / 100).toString() + ' MB. ' + ((charts_failed) ? (charts_failed.toString() + ' charts have failed to be downloaded') : '').toString() + info); - - if (idx > 0) { - update_chart(idx); - } else { - saveData.charts_ok = charts_ok; - saveData.charts_failed = charts_failed; - saveData.data_size = size; - // console.log(saveData.compression + ': ' + (size / (options.data.dimensions_count * Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint))).toString()); - - // save it - // console.log(saveData); - saveObjectToClient(saveData, filename); - - if (charts_failed > 0) { - alert(charts_failed.toString() + ' failed to be downloaded'); - } - - saveSnapshotRestore(); - saveData = null; - } - }) - }, 0); - } - - update_chart(NETDATA.options.targets.length); - }); - }); -} - -// -------------------------------------------------------------------- -// activate netdata on the page - -function dashboardSettingsSetup() { - var update_options_modal = function () { - // console.log('update_options_modal'); - - var sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== NETDATA.getOption(option)) { - // console.log('switching ' + option.toString()); - self.bootstrapToggle(NETDATA.getOption(option) ? 'on' : 'off'); - } - }; - - var theme_sync_option = function (option) { - var self = $('#' + option); - - self.bootstrapToggle(netdataTheme === 'slate' ? 'on' : 'off'); - }; - var units_sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== (NETDATA.getOption('units') === 'auto')) { - self.bootstrapToggle(NETDATA.getOption('units') === 'auto' ? 'on' : 'off'); - } - - if (self.prop('checked') === true) { - $('#settingsLocaleTempRow').show(); - $('#settingsLocaleTimeRow').show(); - } else { - $('#settingsLocaleTempRow').hide(); - $('#settingsLocaleTimeRow').hide(); - } - }; - var temp_sync_option = function (option) { - var self = $('#' + option); - - if (self.prop('checked') !== (NETDATA.getOption('temperature') === 'celsius')) { - self.bootstrapToggle(NETDATA.getOption('temperature') === 'celsius' ? 'on' : 'off'); - } - }; - var timezone_sync_option = function (option) { - var self = $('#' + option); - - document.getElementById('browser_timezone').innerText = NETDATA.options.browser_timezone; - document.getElementById('server_timezone').innerText = NETDATA.options.server_timezone; - document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone; - - if (self.prop('checked') === NETDATA.dateTime.using_timezone) { - self.bootstrapToggle(NETDATA.dateTime.using_timezone ? 'off' : 'on'); - } - }; - - 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'); - sync_option('seconds_as_time'); - theme_sync_option('netdata_theme_control'); - units_sync_option('units_conversion'); - temp_sync_option('units_temp'); - timezone_sync_option('local_timezone'); - - 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')); - }); - $('#seconds_as_time').change(function () { - NETDATA.setOption('seconds_as_time', $(this).prop('checked')); - }); - $('#local_timezone').change(function () { - if ($(this).prop('checked')) { - selected_server_timezone('default', true); - } else { - selected_server_timezone('default', false); - } - }); - - $('#units_conversion').change(function () { - NETDATA.setOption('units', $(this).prop('checked') ? 'auto' : 'original'); - }); - $('#units_temp').change(function () { - NETDATA.setOption('temperature', $(this).prop('checked') ? 'celsius' : 'fahrenheit'); - }); - - $('#show_help').change(function () { - urlOptions.help = $(this).prop('checked'); - urlOptions.hashUpdate(); - - 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'; - urlOptions.hashUpdate(); - - if (setTheme(urlOptions.theme)) { - netdataReload(); - } - }); -} - -function scrollDashboardTo() { - if (netdataSnapshotData !== null && typeof netdataSnapshotData.hash !== 'undefined') { - //console.log(netdataSnapshotData.hash); - scrollToId(netdataSnapshotData.hash.replace('#', '')); - } else { - // 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); - } - } -} - -var modalHiddenCallback = null; - -function scrollToChartAfterHidingModal(chart, alarmDate, alarmStatus) { - modalHiddenCallback = function () { - NETDATA.alarms.scrollToChart(chart, alarmDate); - - if (['WARNING', 'CRITICAL'].includes(alarmStatus)) { - const currentChartState = NETDATA.options.targets.find( - (chartState) => chartState.id === chart, - ) - const twoMinutes = 2 * 60 * 1000 - NETDATA.globalPanAndZoom.setMaster( - currentChartState, - alarmDate - twoMinutes, - alarmDate + twoMinutes, - ) - } - }; -} - -// ---------------------------------------------------------------------------- - -function enableTooltipsAndPopovers() { - $('[data-toggle="tooltip"]').tooltip({ - animated: 'fade', - trigger: 'hover', - html: true, - delay: { show: 500, hide: 0 }, - container: 'body' - }); - $('[data-toggle="popover"]').popover(); -} - -// ---------------------------------------------------------------------------- - -var runOnceOnDashboardLastRun = 0; - -function runOnceOnDashboardWithjQuery() { - if (runOnceOnDashboardLastRun !== 0) { - scrollDashboardTo(); - - // restore the scrollspy at the proper position - $(document.body).scrollspy('refresh'); - $(document.body).scrollspy('process'); - - return; - } - - runOnceOnDashboardLastRun = Date.now(); - - // ------------------------------------------------------------------------ - // bootstrap modals - - // prevent bootstrap modals from scrolling the page - // maintains the current scroll position - // https://stackoverflow.com/a/34754029/4525767 - - var scrollPos = 0; - var modal_depth = 0; // how many modals are currently open - var modal_shown = false; // set to true, if a modal is shown - var netdata_paused_on_modal = false; // set to true, if the modal paused netdata - var scrollspyOffset = $(window).height() / 3; // will be updated below - the offset of scrollspy to select an item - - $('.modal') - .on('show.bs.modal', function () { - if (modal_depth === 0) { - scrollPos = window.scrollY; - - $('body').css({ - overflow: 'hidden', - position: 'fixed', - top: -scrollPos - }); - - modal_shown = true; - - if (NETDATA.options.pauseCallback === null) { - NETDATA.pause(function () { - }); - netdata_paused_on_modal = true; - } else { - netdata_paused_on_modal = false; - } - } - - modal_depth++; - //console.log(urlOptions.after); - - }) - .on('hide.bs.modal', function () { - - modal_depth--; - - if (modal_depth <= 0) { - modal_depth = 0; - - $('body') - .css({ - overflow: '', - position: '', - top: '' - }); - - // scroll to the position we had open before the modal - $('html, body') - .animate({ scrollTop: scrollPos }, 0); - - // unpause netdata, if we paused it - if (netdata_paused_on_modal === true) { - NETDATA.unpause(); - netdata_paused_on_modal = false; - } - - // restore the scrollspy at the proper position - $(document.body).scrollspy('process'); - } - //console.log(urlOptions.after); - }) - .on('hidden.bs.modal', function () { - if (modal_depth === 0) { - modal_shown = false; - } - - if (typeof modalHiddenCallback === 'function') { - modalHiddenCallback(); - } - - modalHiddenCallback = null; - //console.log(urlOptions.after); - }); - - // ------------------------------------------------------------------------ - // sidebar / affix - - if (shouldShowSignInBanner()) { - const el = document.getElementById("sign-in-banner"); - if (el) { - el.style.display = "initial"; - el.classList.add(`theme-${netdataTheme}`); - } - } - - $('#sidebar') - .affix({ - offset: { - top: (isdemo()) ? 150 : 0, - bottom: 0 - } - }) - .on('affixed.bs.affix', function () { - // fix scrolling of very long affix lists - // http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long - - $(this).removeAttr('style'); - }) - .on('affix-top.bs.affix', function () { - // fix bootstrap affix click bug - // https://stackoverflow.com/a/37847981/4525767 - - if (modal_shown) { - return false; - } - }) - .on('activate.bs.scrollspy', function (e) { - // change the URL based on the current position of the screen - - if (modal_shown === false) { - var el = $(e.target); - var hash = el.find('a').attr('href'); - if (typeof hash === 'string' && hash.substring(0, 1) === '#' && urlOptions.hash.startsWith(hash + '_submenu_') === false) { - urlOptions.hash = hash; - urlOptions.hashUpdate(); - } - } - }); - - Ps.initialize(document.getElementById('sidebar'), { - wheelSpeed: 0.5, - wheelPropagation: true, - swipePropagation: true, - minScrollbarLength: null, - maxScrollbarLength: null, - useBothWheelAxes: false, - suppressScrollX: true, - suppressScrollY: false, - scrollXMarginOffset: 0, - scrollYMarginOffset: 0, - theme: 'default' - }); - - // ------------------------------------------------------------------------ - // scrollspy - - if (scrollspyOffset > 250) { - scrollspyOffset = 250; - } - if (scrollspyOffset < 75) { - scrollspyOffset = 75; - } - document.body.setAttribute('data-offset', scrollspyOffset); - - // scroll the dashboard, before activating the scrollspy, so that our - // hash will not be updated before we got the chance to scroll to it - scrollDashboardTo(); - - $(document.body).scrollspy({ - target: '#sidebar', - offset: scrollspyOffset // controls the diff of the element to the top, to select it - }); - - // ------------------------------------------------------------------------ - // my-netdata menu - - Ps.initialize(document.getElementById('my-netdata-dropdown-content'), { - wheelSpeed: 1, - wheelPropagation: false, - swipePropagation: false, - minScrollbarLength: null, - maxScrollbarLength: null, - useBothWheelAxes: false, - suppressScrollX: true, - suppressScrollY: false, - scrollXMarginOffset: 0, - scrollYMarginOffset: 0, - theme: 'default' - }); - - $('#myNetdataDropdownParent') - .on('show.bs.dropdown', function () { - var hash = urlOptions.genHash(); - $('.registry_link').each(function (idx) { - this.setAttribute('href', this.getAttribute("href").replace(/#.*$/, hash)); - }); - - NETDATA.pause(function () { - }); - }) - .on('shown.bs.dropdown', function () { - Ps.update(document.getElementById('my-netdata-dropdown-content')); - myNetdataMenuDidShow(); - }) - .on('hidden.bs.dropdown', function () { - NETDATA.unpause(); - }); - - $('#deleteRegistryModal') - .on('hidden.bs.modal', function () { - deleteRegistryGuid = null; - }); - - // ------------------------------------------------------------------------ - // update modal - - $('#updateModal') - .on('show.bs.modal', function () { - versionLog('checking, please wait...'); - }) - .on('shown.bs.modal', function () { - notifyForUpdate(true); - }); - - // ------------------------------------------------------------------------ - // alarms modal - - $('#alarmsModal') - .on('shown.bs.modal', function () { - alarmsUpdateModal(); - }) - .on('hidden.bs.modal', function () { - document.getElementById('alarms_active').innerHTML = - document.getElementById('alarms_all').innerHTML = - document.getElementById('alarms_log').innerHTML = - 'loading...'; - }); - - // ------------------------------------------------------------------------ - - dashboardSettingsSetup(); - loadSnapshotDragAndDropSetup(); - saveSnapshotModalSetup(); - showPageFooter(); - - // ------------------------------------------------------------------------ - // 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: ' show more information', - lessText: ' show less information', - onLess: function () { - NETDATA.onscroll(); - }, - onMore: function () { - NETDATA.onscroll(); - }, - errMsg: null, - force: false - }; - - if (settings) { - $.extend(config, settings); - } - - if ($(this).data('jquery.shorten') && !config.force) { - return false; - } - $(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); - - 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; - - // This could be "tag" or "/tag" - tagName = content.substring(i + 1, content.indexOf('>', i)); - - // 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!) - } - - } else { - // There are some nasty tags that don't have a close tag like
- 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) { - bag += content.charAt(i); - } else { - // Add tag name chars to the result - 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 - - //console.log('They were open tags'); - //console.log(openTags); - for (var j = 0; j < openTags.length; j++) { - //console.log('Cierro tag ' + openTags[j]); - bag += ''; // 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; - } - } - } - } - c = $('
').html(bag + '' + config.ellipsesText + '').html(); - } else { - c += config.ellipsesText; - } - - var html = '
' + c + - '
' + content + - '
' + config.moreText + ''; - - $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 - } - }); - }; -} - -function finalizePage() { - // resize all charts - without starting the background thread - // this has to be done while NETDATA is paused - // if we omit this, the affix menu will be wrong, since all - // the Dom elements are initially zero-sized - NETDATA.parseDom(); - - // ------------------------------------------------------------------------ - - NETDATA.globalPanAndZoom.callback = null; - NETDATA.globalChartUnderlay.callback = null; - - if (urlOptions.pan_and_zoom === true && NETDATA.options.targets.length > 0) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before); - } - - // callback for us to track PanAndZoom operations - NETDATA.globalPanAndZoom.callback = urlOptions.netdataPanAndZoomCallback; - NETDATA.globalChartUnderlay.callback = urlOptions.netdataHighlightCallback; - - // ------------------------------------------------------------------------ - - // let it run (update the charts) - NETDATA.unpause(); - - runOnceOnDashboardWithjQuery(); - $(".shorten").shorten(); - enableTooltipsAndPopovers(); - - if (isdemo()) { - // do not to give errors on netdata demo servers for 60 seconds - NETDATA.options.current.retries_on_data_failures = 60; - - // 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'); - - ga('create', 'UA-64295674-3', 'auto'); - ga('send', 'pageview', '/demosite/' + window.location.host); - }, 2000); - } else { - notifyForUpdate(); - } - - if (urlOptions.show_alarms === true) { - setTimeout(function () { - $('#alarmsModal').modal('show'); - }, 1000); - } - - NETDATA.onresizeCallback = function () { - Ps.update(document.getElementById('sidebar')); - Ps.update(document.getElementById('my-netdata-dropdown-content')); - }; - NETDATA.onresizeCallback(); - - if (netdataSnapshotData !== null) { - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], netdataSnapshotData.after_ms, netdataSnapshotData.before_ms); - } - - //if (urlOptions.nowelcome !== true) { - // setTimeout(function () { - // $('#welcomeModal').modal(); - // }, 2000); - //} - - // var netdataEnded = performance.now(); - // console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms'); -} - -function resetDashboardOptions() { - var help = NETDATA.options.current.show_help; - - NETDATA.resetOptions(); - if (setTheme('slate')) { - 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.serverStatic + 'css/bootstrap-toggle-2.2.2.min.css', - isAlreadyLoaded: function () { - return false; - } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverStatic + 'lib/bootstrap-toggle-2.2.2.min.js', - isAlreadyLoaded: function () { - return false; - } - }); - - NETDATA.requiredJs.push({ - url: NETDATA.serverStatic + 'dashboard_info.js?v20181019-1', - async: false, - isAlreadyLoaded: function () { - return false; - } - }); - - 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); - } - } -}; - -var selected_server_timezone = function (timezone, status) { - //console.log('called with timezone: ' + timezone + ", status: " + ((typeof status === 'undefined')?'undefined':status).toString()); - - // clear the error - document.getElementById('timezone_error_message').innerHTML = ''; - - if (typeof status === 'undefined') { - // the user selected a timezone from the menu - - NETDATA.setOption('user_set_server_timezone', timezone); - - if (NETDATA.dateTime.init(timezone) === false) { - NETDATA.dateTime.init(); - - if (!$('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('on'); - } - - document.getElementById('timezone_error_message').innerHTML = 'Ooops! That timezone was not accepted by your browser. Please open a github issue to help us fix it.'; - NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone); - } else { - if ($('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('off'); - } - } - } else if (status === true) { - // the user wants the browser default timezone to be activated - - NETDATA.dateTime.init(); - } else { - // the user wants the server default timezone to be activated - //console.log('found ' + NETDATA.options.current.user_set_server_timezone); - - if (NETDATA.options.current.user_set_server_timezone === 'default') { - NETDATA.options.current.user_set_server_timezone = NETDATA.options.server_timezone; - } - - timezone = NETDATA.options.current.user_set_server_timezone; - - if (NETDATA.dateTime.init(timezone) === false) { - NETDATA.dateTime.init(); - - if (!$('#local_timezone').prop('checked')) { - $('#local_timezone').bootstrapToggle('on'); - } - - document.getElementById('timezone_error_message').innerHTML = 'Sorry. The timezone "' + timezone.toString() + '" is not accepted by your browser. Please select one from the list.'; - NETDATA.setOption('user_set_server_timezone', NETDATA.options.server_timezone); - } - } - - document.getElementById('current_timezone').innerText = (NETDATA.options.current.timezone === 'default') ? 'unset, using browser default' : NETDATA.options.current.timezone; - return false; -}; - -// our entry point -// var netdataStarted = performance.now(); - -var netdataCallback = initializeDynamicDashboard; - -// ================================================================================================= -// netdata.cloud - -let registryAgents = []; - -let cloudAgents = []; - -let myNetdataMenuFilterValue = ""; - -let cloudAccountID = null; - -let cloudAccountName = null; - -let cloudToken = null; - -/// Enforces a maximum string length while retaining the prefix and the postfix of -/// the string. -function truncateString(str, maxLength) { - if (str.length <= maxLength) { - return str; - } - - const spanLength = Math.floor((maxLength - 3) / 2); - return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`; -} - -// ------------------------------------------------------------------------------------------------- -// netdata.cloud API Client -// ------------------------------------------------------------------------------------------------- - -function isValidAgent(a) { - return a.urls != null && a.urls.length > 0; -} - -// https://github.com/netdata/hub/issues/146 -function getCloudAccountAgents() { - if (!isSignedIn()) { - return []; - } - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, - { - method: "GET", - mode: "cors", - headers: { - "Authorization": `Bearer ${cloudToken}` - } - } - ).then((response) => { - if (!response.ok) { - throw Error("Cannot fetch known accounts"); - } - return response.json(); - }).then((payload) => { - const agents = payload.result ? payload.result.agents : null; - - if (!agents) { - return []; - } - - return agents.filter((a) => isValidAgent(a)).map((a) => { - return { - "guid": a.id, - "name": a.name, - "url": a.urls[0], - "alternate_urls": a.urls - } - }) - }).catch(function (error) { - console.log(error); - return null; - }); -} - -/** Updates the lastAccessTime and accessCount properties of the agent for the account. */ -function touchAgent() { - if (!isSignedIn()) { - return []; - } - - const touchUrl = `${NETDATA.registry.cloudBaseURL}/api/v1/agents/${NETDATA.registry.machine_guid}/touch?account_id=${cloudAccountID}`; - return fetch( - touchUrl, - { - method: "post", - body: "", - mode: "cors", - headers: { - "Authorization": `Bearer ${cloudToken}` - } - } - ).then((response) => { - if (!response.ok) { - throw Error("Cannot touch agent" + JSON.stringify(response)); - } - return response.json(); - }).then((payload) => { - - }).catch(function (error) { - console.log(error); - return null; - }); -} - -// https://github.com/netdata/hub/issues/128 -function postCloudAccountAgents(agentsToSync) { - if (!isSignedIn()) { - return []; - } - - const maskedURL = NETDATA.registry.MASKED_DATA; - - const agents = agentsToSync.map((a) => { - const urls = a.alternate_urls.filter((url) => url != maskedURL); - - return { - "id": a.guid, - "name": a.name, - "urls": urls - } - }).filter((a) => isValidAgent(a)) - - const payload = { - "accountID": cloudAccountID, - "agents": agents, - "merge": false, - }; - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, - { - method: "POST", - mode: "cors", - headers: { - "Content-Type": "application/json; charset=utf-8", - "Authorization": `Bearer ${cloudToken}` - }, - body: JSON.stringify(payload) - } - ).then((response) => { - return response.json(); - }).then((payload) => { - const agents = payload.result ? payload.result.agents : null; - - if (!agents) { - return []; - } - - return agents.filter((a) => isValidAgent(a)).map((a) => { - return { - "guid": a.id, - "name": a.name, - "url": a.urls[0], - "alternate_urls": a.urls - } - }) - }); -} - -function deleteCloudAgentURL(agentID, url) { - if (!isSignedIn()) { - return []; - } - - return fetch( - `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents/${agentID}/url?value=${encodeURIComponent(url)}`, - { - method: "DELETE", - mode: "cors", - headers: { - "Content-Type": "application/json; charset=utf-8", - "Authorization": `Bearer ${cloudToken}` - }, - } - ).then((response) => { - return response.json(); - }).then((payload) => { - const count = payload.result ? payload.result.count : 0; - return count; - }); -} - -// ------------------------------------------------------------------------------------------------- - -function signInDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - - if (!NETDATA.registry.isUsingGlobalRegistry()) { - // If user is using a private registry, request his consent for - // synchronizing with cloud. - showSignInModal(); - return; - } - - signIn(); -} - -function shouldShowSignInBanner() { - return false; -} - -function closeSignInBanner() { - localStorage.setItem("signInBannerClosed", "true"); - const el = document.getElementById("sign-in-banner"); - if (el) { - el.style.display = "none"; - } -} - -function closeSignInBannerDidClick(e) { - closeSignInBanner(); -} - -function signOutDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - signOut(); -} - -// ------------------------------------------------------------------------------------------------- - -function updateMyNetdataAfterFilterChange() { - const machinesEl = document.getElementById("my-netdata-menu-machines") - machinesEl.innerHTML = renderMachines(cloudAgents); - - if (options.hosts.length > 1) { - const streamedEl = document.getElementById("my-netdata-menu-streamed") - streamedEl.innerHTML = renderStreamedHosts(options); - } -} - -function myNetdataMenuDidShow() { - const filterEl = document.getElementById("my-netdata-menu-filter-input"); - if (filterEl) { - filterEl.focus(); - } -} - -function myNetdataFilterDidChange(e) { - const inputEl = e.target; - setTimeout(() => { - myNetdataMenuFilterValue = inputEl.value; - updateMyNetdataAfterFilterChange(); - }, 1); -} - -function myNetdataFilterClearDidClick(e) { - e.preventDefault(); - e.stopPropagation(); - - const inputEl = document.getElementById("my-netdata-menu-filter-input"); - inputEl.value = ""; - myNetdataMenuFilterValue = ""; - - updateMyNetdataAfterFilterChange(); - - inputEl.focus(); -} - -// ------------------------------------------------------------------------------------------------- - -function clearCloudVariables() { - cloudAccountID = null; - cloudAccountName = null; - cloudToken = null; -} - -function clearCloudLocalStorageItems() { - localStorage.removeItem("cloud.baseURL"); - localStorage.removeItem("cloud.agentID"); - localStorage.removeItem("cloud.sync"); -} - -function signIn() { - const url = `${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}`; - window.open(url); -} - -function signOut() { - cloudSSOSignOut(); -} - -function handleMessage(e) { - switch (e.data.type) { - case "sign-in": - handleSignInMessage(e); - break; - - case "sign-out": - handleSignOutMessage(e); - break; - - default: - return; - } -} - -function handleSignInMessage(e) { - closeSignInBanner(); - localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); - - cloudAccountID = e.data.accountID; - cloudAccountName = e.data.accountName; - cloudToken = e.data.token; - - netdataRegistryCallback(registryAgents); - if (e.data.redirectURI && !window.location.href.includes(e.data.redirectURI)) { - // lgtm false-positive - redirectURI does not come from user input, but from iframe callback - window.location.replace(e.data.redirectURI); // lgtm[js/client-side-unvalidated-url-redirection] - } -} - -function handleSignOutMessage(e) { - clearCloudVariables(); - renderMyNetdataMenu(registryAgents); -} - -function isSignedIn() { - return cloudToken != null && cloudAccountID != null; -} - -function sortedArraysEqual(a, b) { - if (a.length != b.length) return false; - - for (var i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false; - } - - return true; -} - -// If merging is needed returns the merged agents set, otherwise returns null. -function mergeAgents(cloud, local) { - let dirty = false; - - const union = new Map(); - - for (const cagent of cloud) { - union.set(cagent.guid, cagent); - } - - for (const lagent of local) { - const cagent = union.get(lagent.guid); - if (cagent) { - for (const u of lagent.alternate_urls) { - if (u === NETDATA.registry.MASKED_DATA) { // TODO: temp until registry is updated. - continue; - } - - if (!cagent.alternate_urls.includes(u)) { - dirty = true; - cagent.alternate_urls.push(u); - } - } - } else { - dirty = true; - union.set(lagent.guid, lagent); - } - } - - if (dirty) { - return Array.from(union.values()); - } - - return null; -} - -function showSignInModal() { - document.getElementById("sim-registry").innerHTML = NETDATA.registry.server; - $("#signInModal").modal("show"); -} - -function explicitlySignIn() { - $("#signInModal").modal("hide"); - signIn(); -} - -function showSyncModal() { - document.getElementById("sync-registry-modal-registry").innerHTML = NETDATA.registry.server; - $("#syncRegistryModal").modal("show"); -} - -function explicitlySyncAgents() { - $("#syncRegistryModal").modal("hide"); - - const json = localStorage.getItem("cloud.sync"); - const sync = json ? JSON.parse(json) : {}; - delete sync[cloudAccountID]; - localStorage.setItem("cloud.sync", JSON.stringify(sync)); - - NETDATA.registry.init(); -} - -function syncAgents(callback) { - const json = localStorage.getItem("cloud.sync"); - const sync = json ? JSON.parse(json) : {}; - - const currentAgent = { - guid: NETDATA.registry.machine_guid, - name: NETDATA.registry.hostname, - url: NETDATA.serverDefault, - alternate_urls: [NETDATA.serverDefault], - } - - const localAgents = sync[cloudAccountID] - ? [currentAgent] - : registryAgents.concat([currentAgent]); - - console.log("Checking if sync is needed.", localAgents); - - const agentsToSync = mergeAgents(cloudAgents, localAgents); - - if ((!sync[cloudAccountID]) || agentsToSync) { - sync[cloudAccountID] = new Date().getTime(); - localStorage.setItem("cloud.sync", JSON.stringify(sync)); - } - - if (agentsToSync) { - console.log("Synchronizing with netdata.cloud."); - - postCloudAccountAgents(agentsToSync).then((agents) => { - // TODO: clear syncTime on error! - cloudAgents = agents; - callback(cloudAgents); - }); - - return - } - - callback(cloudAgents); -} - -let isCloudSSOInitialized = false; - -function cloudSSOInit() { - const iframeEl = document.getElementById("ssoifrm"); - const url = `${NETDATA.registry.cloudBaseURL}/account/sso-agent?id=${NETDATA.registry.machine_guid}`; - iframeEl.src = url; - isCloudSSOInitialized = true; -} - -function cloudSSOSignOut() { - const iframe = document.getElementById("ssoifrm"); - const url = `${NETDATA.registry.cloudBaseURL}/account/sign-out-agent`; - iframe.src = url; -} - -function initCloud() { - if (!NETDATA.registry.isCloudEnabled) { - clearCloudVariables(); - clearCloudLocalStorageItems(); - return; - } - - if (NETDATA.registry.cloudBaseURL != localStorage.getItem("cloud.baseURL")) { - clearCloudVariables(); - clearCloudLocalStorageItems(); - if (NETDATA.registry.cloudBaseURL) { - localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); - } - } - - if (!isCloudSSOInitialized) { - cloudSSOInit(); - } - - touchAgent(); -} - -// This callback is called after NETDATA.registry is initialized. -function netdataRegistryCallback(machinesArray) { - localStorage.setItem("cloud.agentID", NETDATA.registry.machine_guid); - - initCloud(); - - registryAgents = machinesArray; - - if (isSignedIn()) { - // We call getCloudAccountAgents() here because it requires that - // NETDATA.registry is initialized. - clearMyNetdataMenu(); - getCloudAccountAgents().then((agents) => { - if (!agents) { - errorMyNetdataMenu(); - return; - } - cloudAgents = agents; - syncAgents((agents) => { - const agentsMap = {} - for (const agent of agents) { - agentsMap[agent.guid] = agent; - } - - NETDATA.registry.machines = agentsMap; - NETDATA.registry.machines_array = agents; - - renderMyNetdataMenu(agents); - }); - }); - } else { - renderMyNetdataMenu(machinesArray) - } -}; - -// If we know the cloudBaseURL and agentID from local storage render (eagerly) -// the account ui before receiving the definitive response from the web server. -// This improves the perceived performance. -function tryFastInitCloud() { - const baseURL = localStorage.getItem("cloud.baseURL"); - const agentID = localStorage.getItem("cloud.agentID"); - - if (baseURL && agentID) { - NETDATA.registry.cloudBaseURL = baseURL; - NETDATA.registry.machine_guid = agentID; - NETDATA.registry.isCloudEnabled = true; - - initCloud(); - } -} - -function initializeApp() { - window.addEventListener("message", handleMessage, false); - - // tryFastInitCloud(); -} - -if (document.readyState === "complete") { - initializeApp(); -} else { - document.addEventListener("readystatechange", () => { - if (document.readyState === "complete") { - initializeApp(); - } - }); -}