├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── cfg.example.json ├── control ├── cron ├── builtin.go ├── collector.go ├── ips.go ├── plugin.go └── reporter.go ├── funcs ├── agent.go ├── checker.go ├── common.go ├── cpustat.go ├── dfstat.go ├── diskstats.go ├── du.go ├── funcs.go ├── ifstat.go ├── kernel.go ├── loadavg.go ├── meminfo.go ├── netstat.go ├── portstat.go ├── procs.go ├── snmp.go ├── sockstat.go └── urlstat.go ├── g ├── cfg.go ├── const.go ├── g.go ├── rpc.go ├── tool.go ├── transfer.go └── var.go ├── http ├── admin.go ├── cpu.go ├── df.go ├── health.go ├── http.go ├── iostat.go ├── kernel.go ├── memory.go ├── page.go ├── plugin.go ├── push.go ├── run.go └── system.go ├── main.go ├── plugins ├── plugins.go ├── reader.go └── scheduler.go └── public ├── css ├── bootstrap-responsive.min.css ├── bootstrap.min.css ├── font-awesome │ ├── css │ │ ├── archive │ │ │ ├── font-awesome-ie7.css │ │ │ ├── font-awesome-ie7.min.css │ │ │ └── font-awesome.css │ │ └── font-awesome.min.css │ └── font │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff ├── font │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ ├── OpenSans-Regular-webfont.ttf │ └── OpenSans-Regular-webfont.woff ├── g.css ├── odometer.css ├── pages │ └── dashboard.css └── style.css ├── img ├── code.png └── favicon.ico ├── index.html └── js ├── base.js ├── bootstrap.js ├── dashboard.js ├── jquery-ui.min.js ├── jquery.dataTables.min.js ├── jquery.js └── odometer.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | /cfg.json 4 | /agent 5 | /var 6 | /falcon-agent* 7 | /falcon_agent 8 | .DS_Store 9 | *.iml 10 | *.idea 11 | /gitversion 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Open-Falcon 2 | 3 | Copyright (c) 2014-2015 Xiaomi, Inc. All Rights Reserved. 4 | 5 | This product is licensed to you under the Apache License, Version 2.0 (the "License"). 6 | You may not use this product except in compliance with the License. 7 | 8 | This product may include a number of subcomponents with separate copyright notices 9 | and license terms. Your use of these subcomponents is subject to the terms and 10 | conditions of the subcomponent's license, as noted in the LICENSE file. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | falcon-agent 2 | === 3 | 4 | This is a linux monitor agent. Just like zabbix-agent and tcollector. 5 | 6 | 7 | ## Installation 8 | 9 | It is a golang classic project 10 | 11 | ```bash 12 | # set $GOPATH and $GOROOT 13 | mkdir -p $GOPATH/src/github.com/open-falcon 14 | cd $GOPATH/src/github.com/open-falcon 15 | git clone https://github.com/open-falcon/agent.git 16 | cd agent 17 | go get ./... 18 | ./control build 19 | ./control start 20 | 21 | # goto http://localhost:1988 22 | ``` 23 | 24 | I use [linux-dash](https://github.com/afaqurk/linux-dash) as the page theme. 25 | 26 | ## Configuration 27 | 28 | - heartbeat: heartbeat server rpc address 29 | - transfer: transfer rpc address 30 | - ignore: the metrics should ignore 31 | 32 | # Deployment 33 | 34 | http://ulricqin.com/project/ops-updater/ 35 | 36 | -------------------------------------------------------------------------------- /cfg.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": true, 3 | "hostname": "", 4 | "ip": "", 5 | "plugin": { 6 | "enabled": false, 7 | "dir": "./plugin", 8 | "git": "https://github.com/open-falcon/plugin.git", 9 | "logs": "./logs" 10 | }, 11 | "heartbeat": { 12 | "enabled": true, 13 | "addr": "127.0.0.1:6030", 14 | "interval": 60, 15 | "timeout": 1000 16 | }, 17 | "transfer": { 18 | "enabled": true, 19 | "addrs": [ 20 | "127.0.0.1:8433", 21 | "127.0.0.1:8433" 22 | ], 23 | "interval": 60, 24 | "timeout": 1000 25 | }, 26 | "http": { 27 | "enabled": true, 28 | "listen": ":1988", 29 | "backdoor": false 30 | }, 31 | "collector": { 32 | "ifacePrefix": ["eth", "em"] 33 | }, 34 | "ignore": { 35 | "cpu.busy": true, 36 | "df.bytes.free": true, 37 | "df.bytes.total": true, 38 | "df.bytes.used": true, 39 | "df.bytes.used.percent": true, 40 | "df.inodes.total": true, 41 | "df.inodes.free": true, 42 | "df.inodes.used": true, 43 | "df.inodes.used.percent": true, 44 | "mem.memtotal": true, 45 | "mem.memused": true, 46 | "mem.memused.percent": true, 47 | "mem.memfree": true, 48 | "mem.swaptotal": true, 49 | "mem.swapused": true, 50 | "mem.swapfree": true 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKSPACE=$(cd $(dirname $0)/; pwd) 4 | cd $WORKSPACE 5 | 6 | mkdir -p var 7 | 8 | module=agent 9 | app=falcon-$module 10 | conf=cfg.json 11 | pidfile=var/app.pid 12 | logfile=var/app.log 13 | 14 | function check_pid() { 15 | if [ -f $pidfile ];then 16 | pid=`cat $pidfile` 17 | if [ -n $pid ]; then 18 | running=`ps -p $pid|grep -v "PID TTY" |wc -l` 19 | return $running 20 | fi 21 | fi 22 | return 0 23 | } 24 | 25 | function start() { 26 | check_pid 27 | running=$? 28 | if [ $running -gt 0 ];then 29 | echo -n "$app now is running already, pid=" 30 | cat $pidfile 31 | return 1 32 | fi 33 | 34 | if ! [ -f $conf ];then 35 | echo "Config file $conf doesn't exist, creating one." 36 | cp cfg.example.json $conf 37 | fi 38 | nohup ./$app -c $conf &> $logfile & 39 | sleep 1 40 | running=`ps -p $! | grep -v "PID TTY" | wc -l` 41 | if [ $running -gt 0 ];then 42 | echo $! > $pidfile 43 | echo "$app started..., pid=$!" 44 | else 45 | echo "$app failed to start." 46 | return 1 47 | fi 48 | } 49 | 50 | function stop() { 51 | pid=`cat $pidfile` 52 | kill $pid 53 | rm -f $pidfile 54 | echo "$app stoped..." 55 | } 56 | 57 | function restart() { 58 | stop 59 | sleep 1 60 | start 61 | } 62 | 63 | function status() { 64 | check_pid 65 | running=$? 66 | if [ $running -gt 0 ];then 67 | echo started 68 | else 69 | echo stoped 70 | fi 71 | } 72 | 73 | function tailf() { 74 | tail -f $logfile 75 | } 76 | 77 | function build() { 78 | go build 79 | if [ $? -ne 0 ]; then 80 | exit $? 81 | fi 82 | mv $module $app 83 | ./$app -v 84 | } 85 | 86 | function pack() { 87 | build 88 | git log -1 --pretty=%h > gitversion 89 | version=`./$app -v` 90 | file_list="public control cfg.example.json $app" 91 | echo "...tar $app-$version.tar.gz <= $file_list" 92 | tar zcf $app-$version.tar.gz gitversion $file_list 93 | } 94 | 95 | function packbin() { 96 | build 97 | git log -1 --pretty=%h > gitversion 98 | version=`./$app -v` 99 | tar zcvf $app-bin-$version.tar.gz $app gitversion 100 | } 101 | 102 | function help() { 103 | echo "$0 build|pack|start|stop|restart|status|tail" 104 | } 105 | 106 | if [ "$1" == "" ]; then 107 | help 108 | elif [ "$1" == "stop" ];then 109 | stop 110 | elif [ "$1" == "start" ];then 111 | start 112 | elif [ "$1" == "restart" ];then 113 | restart 114 | elif [ "$1" == "status" ];then 115 | status 116 | elif [ "$1" == "tail" ];then 117 | tailf 118 | elif [ "$1" == "build" ];then 119 | build 120 | elif [ "$1" == "pack" ];then 121 | pack 122 | elif [ "$1" == "packbin" ];then 123 | packbin 124 | else 125 | help 126 | fi 127 | -------------------------------------------------------------------------------- /cron/builtin.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | "log" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func SyncBuiltinMetrics() { 13 | if g.Config().Heartbeat.Enabled && g.Config().Heartbeat.Addr != "" { 14 | go syncBuiltinMetrics() 15 | } 16 | } 17 | 18 | func syncBuiltinMetrics() { 19 | 20 | var timestamp int64 = -1 21 | var checksum string = "nil" 22 | 23 | duration := time.Duration(g.Config().Heartbeat.Interval) * time.Second 24 | 25 | for { 26 | time.Sleep(duration) 27 | 28 | var ports = []int64{} 29 | var paths = []string{} 30 | var procs = make(map[string]map[int]string) 31 | var urls = make(map[string]string) 32 | 33 | hostname, err := g.Hostname() 34 | if err != nil { 35 | continue 36 | } 37 | 38 | req := model.AgentHeartbeatRequest{ 39 | Hostname: hostname, 40 | Checksum: checksum, 41 | } 42 | 43 | var resp model.BuiltinMetricResponse 44 | err = g.HbsClient.Call("Agent.BuiltinMetrics", req, &resp) 45 | if err != nil { 46 | log.Println("ERROR:", err) 47 | continue 48 | } 49 | 50 | if resp.Timestamp <= timestamp { 51 | continue 52 | } 53 | 54 | if resp.Checksum == checksum { 55 | continue 56 | } 57 | 58 | timestamp = resp.Timestamp 59 | checksum = resp.Checksum 60 | 61 | for _, metric := range resp.Metrics { 62 | 63 | if metric.Metric == g.URL_CHECK_HEALTH { 64 | arr := strings.Split(metric.Tags, ",") 65 | if len(arr) != 2 { 66 | continue 67 | } 68 | url := strings.Split(arr[0], "=") 69 | if len(url) != 2 { 70 | continue 71 | } 72 | stime := strings.Split(arr[1], "=") 73 | if len(stime) != 2 { 74 | continue 75 | } 76 | if _, err := strconv.ParseInt(stime[1], 10, 64); err == nil { 77 | urls[url[1]] = stime[1] 78 | } else { 79 | log.Println("metric ParseInt timeout failed:", err) 80 | } 81 | } 82 | 83 | if metric.Metric == g.NET_PORT_LISTEN { 84 | arr := strings.Split(metric.Tags, "=") 85 | if len(arr) != 2 { 86 | continue 87 | } 88 | 89 | if port, err := strconv.ParseInt(arr[1], 10, 64); err == nil { 90 | ports = append(ports, port) 91 | } else { 92 | log.Println("metrics ParseInt failed:", err) 93 | } 94 | 95 | continue 96 | } 97 | 98 | if metric.Metric == g.DU_BS { 99 | arr := strings.Split(metric.Tags, "=") 100 | if len(arr) != 2 { 101 | continue 102 | } 103 | 104 | paths = append(paths, strings.TrimSpace(arr[1])) 105 | continue 106 | } 107 | 108 | if metric.Metric == g.PROC_NUM { 109 | arr := strings.Split(metric.Tags, ",") 110 | 111 | tmpMap := make(map[int]string) 112 | 113 | for i := 0; i < len(arr); i++ { 114 | if strings.HasPrefix(arr[i], "name=") { 115 | tmpMap[1] = strings.TrimSpace(arr[i][5:]) 116 | } else if strings.HasPrefix(arr[i], "cmdline=") { 117 | tmpMap[2] = strings.TrimSpace(arr[i][8:]) 118 | } 119 | } 120 | 121 | procs[metric.Tags] = tmpMap 122 | } 123 | } 124 | 125 | g.SetReportUrls(urls) 126 | g.SetReportPorts(ports) 127 | g.SetReportProcs(procs) 128 | g.SetDuPaths(paths) 129 | 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cron/collector.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/open-falcon/agent/funcs" 7 | "github.com/open-falcon/agent/g" 8 | "github.com/open-falcon/common/model" 9 | ) 10 | 11 | func InitDataHistory() { 12 | for { 13 | funcs.UpdateCpuStat() 14 | funcs.UpdateDiskStats() 15 | time.Sleep(g.COLLECT_INTERVAL) 16 | } 17 | } 18 | 19 | func Collect() { 20 | 21 | if !g.Config().Transfer.Enabled { 22 | return 23 | } 24 | 25 | if len(g.Config().Transfer.Addrs) == 0 { 26 | return 27 | } 28 | 29 | for _, v := range funcs.Mappers { 30 | go collect(int64(v.Interval), v.Fs) 31 | } 32 | } 33 | 34 | func collect(sec int64, fns []func() []*model.MetricValue) { 35 | t := time.NewTicker(time.Second * time.Duration(sec)).C 36 | for { 37 | <-t 38 | 39 | hostname, err := g.Hostname() 40 | if err != nil { 41 | continue 42 | } 43 | 44 | mvs := []*model.MetricValue{} 45 | ignoreMetrics := g.Config().IgnoreMetrics 46 | 47 | for _, fn := range fns { 48 | items := fn() 49 | if items == nil { 50 | continue 51 | } 52 | 53 | if len(items) == 0 { 54 | continue 55 | } 56 | 57 | for _, mv := range items { 58 | if b, ok := ignoreMetrics[mv.Metric]; ok && b { 59 | continue 60 | } else { 61 | mvs = append(mvs, mv) 62 | } 63 | } 64 | } 65 | 66 | now := time.Now().Unix() 67 | for j := 0; j < len(mvs); j++ { 68 | mvs[j].Step = sec 69 | mvs[j].Endpoint = hostname 70 | mvs[j].Timestamp = now 71 | } 72 | 73 | g.SendToTransfer(mvs) 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cron/ips.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | "log" 7 | "time" 8 | ) 9 | 10 | func SyncTrustableIps() { 11 | if g.Config().Heartbeat.Enabled && g.Config().Heartbeat.Addr != "" { 12 | go syncTrustableIps() 13 | } 14 | } 15 | 16 | func syncTrustableIps() { 17 | 18 | duration := time.Duration(g.Config().Heartbeat.Interval) * time.Second 19 | 20 | for { 21 | time.Sleep(duration) 22 | 23 | var ips string 24 | err := g.HbsClient.Call("Agent.TrustableIps", model.NullRpcRequest{}, &ips) 25 | if err != nil { 26 | log.Println("ERROR: call Agent.TrustableIps fail", err) 27 | continue 28 | } 29 | 30 | g.SetTrustableIps(ips) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cron/plugin.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/agent/plugins" 6 | "github.com/open-falcon/common/model" 7 | "log" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func SyncMinePlugins() { 13 | if !g.Config().Plugin.Enabled { 14 | return 15 | } 16 | 17 | if !g.Config().Heartbeat.Enabled { 18 | return 19 | } 20 | 21 | if g.Config().Heartbeat.Addr == "" { 22 | return 23 | } 24 | 25 | go syncMinePlugins() 26 | } 27 | 28 | func syncMinePlugins() { 29 | 30 | var ( 31 | timestamp int64 = -1 32 | pluginDirs []string 33 | ) 34 | 35 | duration := time.Duration(g.Config().Heartbeat.Interval) * time.Second 36 | 37 | for { 38 | time.Sleep(duration) 39 | 40 | hostname, err := g.Hostname() 41 | if err != nil { 42 | continue 43 | } 44 | 45 | req := model.AgentHeartbeatRequest{ 46 | Hostname: hostname, 47 | } 48 | 49 | var resp model.AgentPluginsResponse 50 | err = g.HbsClient.Call("Agent.MinePlugins", req, &resp) 51 | if err != nil { 52 | log.Println("ERROR:", err) 53 | continue 54 | } 55 | 56 | if resp.Timestamp <= timestamp { 57 | continue 58 | } 59 | 60 | pluginDirs = resp.Plugins 61 | timestamp = resp.Timestamp 62 | 63 | if g.Config().Debug { 64 | log.Println(&resp) 65 | } 66 | 67 | if len(pluginDirs) == 0 { 68 | plugins.ClearAllPlugins() 69 | } 70 | 71 | desiredAll := make(map[string]*plugins.Plugin) 72 | 73 | for _, p := range pluginDirs { 74 | underOneDir := plugins.ListPlugins(strings.Trim(p, "/")) 75 | for k, v := range underOneDir { 76 | desiredAll[k] = v 77 | } 78 | } 79 | 80 | plugins.DelNoUsePlugins(desiredAll) 81 | plugins.AddNewPlugins(desiredAll) 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cron/reporter.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/agent/g" 6 | "github.com/open-falcon/common/model" 7 | "log" 8 | "time" 9 | ) 10 | 11 | func ReportAgentStatus() { 12 | if g.Config().Heartbeat.Enabled && g.Config().Heartbeat.Addr != "" { 13 | go reportAgentStatus(time.Duration(g.Config().Heartbeat.Interval) * time.Second) 14 | } 15 | } 16 | 17 | func reportAgentStatus(interval time.Duration) { 18 | for { 19 | hostname, err := g.Hostname() 20 | if err != nil { 21 | hostname = fmt.Sprintf("error:%s", err.Error()) 22 | } 23 | 24 | req := model.AgentReportRequest{ 25 | Hostname: hostname, 26 | IP: g.IP(), 27 | AgentVersion: g.VERSION, 28 | PluginVersion: g.GetCurrPluginVersion(), 29 | } 30 | 31 | var resp model.SimpleRpcResponse 32 | err = g.HbsClient.Call("Agent.ReportStatus", req, &resp) 33 | if err != nil || resp.Code != 0 { 34 | log.Println("call Agent.ReportStatus fail:", err, "Request:", req, "Response:", resp) 35 | } 36 | 37 | time.Sleep(interval) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /funcs/agent.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | ) 6 | 7 | func AgentMetrics() []*model.MetricValue { 8 | return []*model.MetricValue{GaugeValue("agent.alive", 1)} 9 | } 10 | -------------------------------------------------------------------------------- /funcs/checker.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "fmt" 5 | "github.com/toolkits/nux" 6 | "github.com/toolkits/sys" 7 | ) 8 | 9 | func CheckCollector() { 10 | 11 | output := make(map[string]bool) 12 | 13 | _, procStatErr := nux.CurrentProcStat() 14 | _, listDiskErr := nux.ListDiskStats() 15 | ports, listeningPortsErr := nux.ListeningPorts() 16 | procs, psErr := nux.AllProcs() 17 | 18 | _, duErr := sys.CmdOut("du", "--help") 19 | 20 | output["kernel "] = len(KernelMetrics()) > 0 21 | output["df.bytes"] = len(DeviceMetrics()) > 0 22 | output["net.if "] = len(CoreNetMetrics([]string{})) > 0 23 | output["loadavg "] = len(LoadAvgMetrics()) > 0 24 | output["cpustat "] = procStatErr == nil 25 | output["disk.io "] = listDiskErr == nil 26 | output["memory "] = len(MemMetrics()) > 0 27 | output["netstat "] = len(NetstatMetrics()) > 0 28 | output["ss -s "] = len(SocketStatSummaryMetrics()) > 0 29 | output["ss -tln "] = listeningPortsErr == nil && len(ports) > 0 30 | output["ps aux "] = psErr == nil && len(procs) > 0 31 | output["du -bs "] = duErr == nil 32 | 33 | for k, v := range output { 34 | status := "fail" 35 | if v { 36 | status = "ok" 37 | } 38 | fmt.Println(k, "...", status) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /funcs/common.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "strings" 6 | ) 7 | 8 | func NewMetricValue(metric string, val interface{}, dataType string, tags ...string) *model.MetricValue { 9 | mv := model.MetricValue{ 10 | Metric: metric, 11 | Value: val, 12 | Type: dataType, 13 | } 14 | 15 | size := len(tags) 16 | 17 | if size > 0 { 18 | mv.Tags = strings.Join(tags, ",") 19 | } 20 | 21 | return &mv 22 | } 23 | 24 | func GaugeValue(metric string, val interface{}, tags ...string) *model.MetricValue { 25 | return NewMetricValue(metric, val, "GAUGE", tags...) 26 | } 27 | 28 | func CounterValue(metric string, val interface{}, tags ...string) *model.MetricValue { 29 | return NewMetricValue(metric, val, "COUNTER", tags...) 30 | } 31 | -------------------------------------------------------------------------------- /funcs/cpustat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "sync" 7 | ) 8 | 9 | const ( 10 | historyCount int = 2 11 | ) 12 | 13 | var ( 14 | procStatHistory [historyCount]*nux.ProcStat 15 | psLock = new(sync.RWMutex) 16 | ) 17 | 18 | func UpdateCpuStat() error { 19 | ps, err := nux.CurrentProcStat() 20 | if err != nil { 21 | return err 22 | } 23 | 24 | psLock.Lock() 25 | defer psLock.Unlock() 26 | for i := historyCount - 1; i > 0; i-- { 27 | procStatHistory[i] = procStatHistory[i-1] 28 | } 29 | 30 | procStatHistory[0] = ps 31 | return nil 32 | } 33 | 34 | func deltaTotal() uint64 { 35 | if procStatHistory[1] == nil { 36 | return 0 37 | } 38 | return procStatHistory[0].Cpu.Total - procStatHistory[1].Cpu.Total 39 | } 40 | 41 | func CpuIdle() float64 { 42 | psLock.RLock() 43 | defer psLock.RUnlock() 44 | dt := deltaTotal() 45 | if dt == 0 { 46 | return 0.0 47 | } 48 | invQuotient := 100.00 / float64(dt) 49 | return float64(procStatHistory[0].Cpu.Idle-procStatHistory[1].Cpu.Idle) * invQuotient 50 | } 51 | 52 | func CpuUser() float64 { 53 | psLock.RLock() 54 | defer psLock.RUnlock() 55 | dt := deltaTotal() 56 | if dt == 0 { 57 | return 0.0 58 | } 59 | invQuotient := 100.00 / float64(dt) 60 | return float64(procStatHistory[0].Cpu.User-procStatHistory[1].Cpu.User) * invQuotient 61 | } 62 | 63 | func CpuNice() float64 { 64 | psLock.RLock() 65 | defer psLock.RUnlock() 66 | dt := deltaTotal() 67 | if dt == 0 { 68 | return 0.0 69 | } 70 | invQuotient := 100.00 / float64(dt) 71 | return float64(procStatHistory[0].Cpu.Nice-procStatHistory[1].Cpu.Nice) * invQuotient 72 | } 73 | 74 | func CpuSystem() float64 { 75 | psLock.RLock() 76 | defer psLock.RUnlock() 77 | dt := deltaTotal() 78 | if dt == 0 { 79 | return 0.0 80 | } 81 | invQuotient := 100.00 / float64(dt) 82 | return float64(procStatHistory[0].Cpu.System-procStatHistory[1].Cpu.System) * invQuotient 83 | } 84 | 85 | func CpuIowait() float64 { 86 | psLock.RLock() 87 | defer psLock.RUnlock() 88 | dt := deltaTotal() 89 | if dt == 0 { 90 | return 0.0 91 | } 92 | invQuotient := 100.00 / float64(dt) 93 | return float64(procStatHistory[0].Cpu.Iowait-procStatHistory[1].Cpu.Iowait) * invQuotient 94 | } 95 | 96 | func CpuIrq() float64 { 97 | psLock.RLock() 98 | defer psLock.RUnlock() 99 | dt := deltaTotal() 100 | if dt == 0 { 101 | return 0.0 102 | } 103 | invQuotient := 100.00 / float64(dt) 104 | return float64(procStatHistory[0].Cpu.Irq-procStatHistory[1].Cpu.Irq) * invQuotient 105 | } 106 | 107 | func CpuSoftIrq() float64 { 108 | psLock.RLock() 109 | defer psLock.RUnlock() 110 | dt := deltaTotal() 111 | if dt == 0 { 112 | return 0.0 113 | } 114 | invQuotient := 100.00 / float64(dt) 115 | return float64(procStatHistory[0].Cpu.SoftIrq-procStatHistory[1].Cpu.SoftIrq) * invQuotient 116 | } 117 | 118 | func CpuSteal() float64 { 119 | psLock.RLock() 120 | defer psLock.RUnlock() 121 | dt := deltaTotal() 122 | if dt == 0 { 123 | return 0.0 124 | } 125 | invQuotient := 100.00 / float64(dt) 126 | return float64(procStatHistory[0].Cpu.Steal-procStatHistory[1].Cpu.Steal) * invQuotient 127 | } 128 | 129 | func CpuGuest() float64 { 130 | psLock.RLock() 131 | defer psLock.RUnlock() 132 | dt := deltaTotal() 133 | if dt == 0 { 134 | return 0.0 135 | } 136 | invQuotient := 100.00 / float64(dt) 137 | return float64(procStatHistory[0].Cpu.Guest-procStatHistory[1].Cpu.Guest) * invQuotient 138 | } 139 | 140 | func CurrentCpuSwitches() uint64 { 141 | psLock.RLock() 142 | defer psLock.RUnlock() 143 | return procStatHistory[0].Ctxt 144 | } 145 | 146 | func CpuPrepared() bool { 147 | psLock.RLock() 148 | defer psLock.RUnlock() 149 | return procStatHistory[1] != nil 150 | } 151 | 152 | func CpuMetrics() []*model.MetricValue { 153 | if !CpuPrepared() { 154 | return []*model.MetricValue{} 155 | } 156 | 157 | cpuIdleVal := CpuIdle() 158 | idle := GaugeValue("cpu.idle", cpuIdleVal) 159 | busy := GaugeValue("cpu.busy", 100.0-cpuIdleVal) 160 | user := GaugeValue("cpu.user", CpuUser()) 161 | nice := GaugeValue("cpu.nice", CpuNice()) 162 | system := GaugeValue("cpu.system", CpuSystem()) 163 | iowait := GaugeValue("cpu.iowait", CpuIowait()) 164 | irq := GaugeValue("cpu.irq", CpuIrq()) 165 | softirq := GaugeValue("cpu.softirq", CpuSoftIrq()) 166 | steal := GaugeValue("cpu.steal", CpuSteal()) 167 | guest := GaugeValue("cpu.guest", CpuGuest()) 168 | switches := CounterValue("cpu.switches", CurrentCpuSwitches()) 169 | return []*model.MetricValue{idle, busy, user, nice, system, iowait, irq, softirq, steal, guest, switches} 170 | } 171 | -------------------------------------------------------------------------------- /funcs/dfstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/common/model" 6 | "github.com/toolkits/nux" 7 | "log" 8 | ) 9 | 10 | func DeviceMetrics() (L []*model.MetricValue) { 11 | mountPoints, err := nux.ListMountPoint() 12 | 13 | if err != nil { 14 | log.Println(err) 15 | return 16 | } 17 | 18 | var diskTotal uint64 = 0 19 | var diskUsed uint64 = 0 20 | 21 | for idx := range mountPoints { 22 | var du *nux.DeviceUsage 23 | du, err = nux.BuildDeviceUsage(mountPoints[idx][0], mountPoints[idx][1], mountPoints[idx][2]) 24 | if err != nil { 25 | log.Println(err) 26 | continue 27 | } 28 | 29 | diskTotal += du.BlocksAll 30 | diskUsed += du.BlocksUsed 31 | 32 | tags := fmt.Sprintf("mount=%s,fstype=%s", du.FsFile, du.FsVfstype) 33 | L = append(L, GaugeValue("df.bytes.total", du.BlocksAll, tags)) 34 | L = append(L, GaugeValue("df.bytes.used", du.BlocksUsed, tags)) 35 | L = append(L, GaugeValue("df.bytes.free", du.BlocksFree, tags)) 36 | L = append(L, GaugeValue("df.bytes.used.percent", du.BlocksUsedPercent, tags)) 37 | L = append(L, GaugeValue("df.bytes.free.percent", du.BlocksFreePercent, tags)) 38 | L = append(L, GaugeValue("df.inodes.total", du.InodesAll, tags)) 39 | L = append(L, GaugeValue("df.inodes.used", du.InodesUsed, tags)) 40 | L = append(L, GaugeValue("df.inodes.free", du.InodesFree, tags)) 41 | L = append(L, GaugeValue("df.inodes.used.percent", du.InodesUsedPercent, tags)) 42 | L = append(L, GaugeValue("df.inodes.free.percent", du.InodesFreePercent, tags)) 43 | 44 | } 45 | 46 | if len(L) > 0 && diskTotal > 0 { 47 | L = append(L, GaugeValue("df.statistics.total", float64(diskTotal))) 48 | L = append(L, GaugeValue("df.statistics.used", float64(diskUsed))) 49 | L = append(L, GaugeValue("df.statistics.used.percent", float64(diskUsed)*100.0/float64(diskTotal))) 50 | } 51 | 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /funcs/diskstats.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/common/model" 6 | "github.com/toolkits/nux" 7 | "log" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | var ( 13 | diskStatsMap = make(map[string][2]*nux.DiskStats) 14 | dsLock = new(sync.RWMutex) 15 | ) 16 | 17 | func UpdateDiskStats() error { 18 | dsList, err := nux.ListDiskStats() 19 | if err != nil { 20 | return err 21 | } 22 | 23 | dsLock.Lock() 24 | defer dsLock.Unlock() 25 | for i := 0; i < len(dsList); i++ { 26 | device := dsList[i].Device 27 | diskStatsMap[device] = [2]*nux.DiskStats{dsList[i], diskStatsMap[device][0]} 28 | } 29 | return nil 30 | } 31 | 32 | func IOReadRequests(arr [2]*nux.DiskStats) uint64 { 33 | return arr[0].ReadRequests - arr[1].ReadRequests 34 | } 35 | 36 | func IOReadMerged(arr [2]*nux.DiskStats) uint64 { 37 | return arr[0].ReadMerged - arr[1].ReadMerged 38 | } 39 | 40 | func IOReadSectors(arr [2]*nux.DiskStats) uint64 { 41 | return arr[0].ReadSectors - arr[1].ReadSectors 42 | } 43 | 44 | func IOMsecRead(arr [2]*nux.DiskStats) uint64 { 45 | return arr[0].MsecRead - arr[1].MsecRead 46 | } 47 | 48 | func IOWriteRequests(arr [2]*nux.DiskStats) uint64 { 49 | return arr[0].WriteRequests - arr[1].WriteRequests 50 | } 51 | 52 | func IOWriteMerged(arr [2]*nux.DiskStats) uint64 { 53 | return arr[0].WriteMerged - arr[1].WriteMerged 54 | } 55 | 56 | func IOWriteSectors(arr [2]*nux.DiskStats) uint64 { 57 | return arr[0].WriteSectors - arr[1].WriteSectors 58 | } 59 | 60 | func IOMsecWrite(arr [2]*nux.DiskStats) uint64 { 61 | return arr[0].MsecWrite - arr[1].MsecWrite 62 | } 63 | 64 | func IOMsecTotal(arr [2]*nux.DiskStats) uint64 { 65 | return arr[0].MsecTotal - arr[1].MsecTotal 66 | } 67 | 68 | func IOMsecWeightedTotal(arr [2]*nux.DiskStats) uint64 { 69 | return arr[0].MsecWeightedTotal - arr[1].MsecWeightedTotal 70 | } 71 | 72 | func TS(arr [2]*nux.DiskStats) uint64 { 73 | return uint64(arr[0].TS.Sub(arr[1].TS).Nanoseconds() / 1000000) 74 | } 75 | 76 | func IODelta(device string, f func([2]*nux.DiskStats) uint64) uint64 { 77 | val, ok := diskStatsMap[device] 78 | if !ok { 79 | return 0 80 | } 81 | 82 | if val[1] == nil { 83 | return 0 84 | } 85 | return f(val) 86 | } 87 | 88 | func DiskIOMetrics() (L []*model.MetricValue) { 89 | 90 | dsList, err := nux.ListDiskStats() 91 | if err != nil { 92 | log.Println(err) 93 | return 94 | } 95 | 96 | for _, ds := range dsList { 97 | if !ShouldHandleDevice(ds.Device) { 98 | continue 99 | } 100 | 101 | device := "device=" + ds.Device 102 | 103 | L = append(L, CounterValue("disk.io.read_requests", ds.ReadRequests, device)) 104 | L = append(L, CounterValue("disk.io.read_merged", ds.ReadMerged, device)) 105 | L = append(L, CounterValue("disk.io.read_sectors", ds.ReadSectors, device)) 106 | L = append(L, CounterValue("disk.io.msec_read", ds.MsecRead, device)) 107 | L = append(L, CounterValue("disk.io.write_requests", ds.WriteRequests, device)) 108 | L = append(L, CounterValue("disk.io.write_merged", ds.WriteMerged, device)) 109 | L = append(L, CounterValue("disk.io.write_sectors", ds.WriteSectors, device)) 110 | L = append(L, CounterValue("disk.io.msec_write", ds.MsecWrite, device)) 111 | L = append(L, CounterValue("disk.io.ios_in_progress", ds.IosInProgress, device)) 112 | L = append(L, CounterValue("disk.io.msec_total", ds.MsecTotal, device)) 113 | L = append(L, CounterValue("disk.io.msec_weighted_total", ds.MsecWeightedTotal, device)) 114 | } 115 | return 116 | } 117 | 118 | func IOStatsMetrics() (L []*model.MetricValue) { 119 | dsLock.RLock() 120 | defer dsLock.RUnlock() 121 | 122 | for device, _ := range diskStatsMap { 123 | if !ShouldHandleDevice(device) { 124 | continue 125 | } 126 | 127 | tags := "device=" + device 128 | rio := IODelta(device, IOReadRequests) 129 | wio := IODelta(device, IOWriteRequests) 130 | delta_rsec := IODelta(device, IOReadSectors) 131 | delta_wsec := IODelta(device, IOWriteSectors) 132 | ruse := IODelta(device, IOMsecRead) 133 | wuse := IODelta(device, IOMsecWrite) 134 | use := IODelta(device, IOMsecTotal) 135 | n_io := rio + wio 136 | avgrq_sz := 0.0 137 | await := 0.0 138 | svctm := 0.0 139 | if n_io != 0 { 140 | avgrq_sz = float64(delta_rsec+delta_wsec) / float64(n_io) 141 | await = float64(ruse+wuse) / float64(n_io) 142 | svctm = float64(use) / float64(n_io) 143 | } 144 | 145 | duration := IODelta(device, TS) 146 | 147 | L = append(L, GaugeValue("disk.io.read_bytes", float64(delta_rsec)*512.0, tags)) 148 | L = append(L, GaugeValue("disk.io.write_bytes", float64(delta_wsec)*512.0, tags)) 149 | L = append(L, GaugeValue("disk.io.avgrq_sz", avgrq_sz, tags)) 150 | L = append(L, GaugeValue("disk.io.avgqu-sz", float64(IODelta(device, IOMsecWeightedTotal))/1000.0, tags)) 151 | L = append(L, GaugeValue("disk.io.await", await, tags)) 152 | L = append(L, GaugeValue("disk.io.svctm", svctm, tags)) 153 | tmp := float64(use) * 100.0 / float64(duration) 154 | if tmp > 100.0 { 155 | tmp = 100.0 156 | } 157 | L = append(L, GaugeValue("disk.io.util", tmp, tags)) 158 | } 159 | 160 | return 161 | } 162 | 163 | func IOStatsForPage() (L [][]string) { 164 | dsLock.RLock() 165 | defer dsLock.RUnlock() 166 | 167 | for device, _ := range diskStatsMap { 168 | if !ShouldHandleDevice(device) { 169 | continue 170 | } 171 | 172 | rio := IODelta(device, IOReadRequests) 173 | wio := IODelta(device, IOWriteRequests) 174 | 175 | delta_rsec := IODelta(device, IOReadSectors) 176 | delta_wsec := IODelta(device, IOWriteSectors) 177 | 178 | ruse := IODelta(device, IOMsecRead) 179 | wuse := IODelta(device, IOMsecWrite) 180 | use := IODelta(device, IOMsecTotal) 181 | n_io := rio + wio 182 | avgrq_sz := 0.0 183 | await := 0.0 184 | svctm := 0.0 185 | if n_io != 0 { 186 | avgrq_sz = float64(delta_rsec+delta_wsec) / float64(n_io) 187 | await = float64(ruse+wuse) / float64(n_io) 188 | svctm = float64(use) / float64(n_io) 189 | } 190 | 191 | item := []string{ 192 | device, 193 | fmt.Sprintf("%d", IODelta(device, IOReadMerged)), 194 | fmt.Sprintf("%d", IODelta(device, IOWriteMerged)), 195 | fmt.Sprintf("%d", rio), 196 | fmt.Sprintf("%d", wio), 197 | fmt.Sprintf("%.2f", float64(delta_rsec)/2.0), 198 | fmt.Sprintf("%.2f", float64(delta_wsec)/2.0), 199 | fmt.Sprintf("%.2f", avgrq_sz), // avgrq-sz: delta(rsect+wsect)/delta(rio+wio) 200 | fmt.Sprintf("%.2f", float64(IODelta(device, IOMsecWeightedTotal))/1000.0), // avgqu-sz: delta(aveq)/s/1000 201 | fmt.Sprintf("%.2f", await), // await: delta(ruse+wuse)/delta(rio+wio) 202 | fmt.Sprintf("%.2f", svctm), // svctm: delta(use)/delta(rio+wio) 203 | fmt.Sprintf("%.2f%%", float64(use)/10.0), // %util: delta(use)/s/1000 * 100% 204 | } 205 | L = append(L, item) 206 | } 207 | 208 | return 209 | } 210 | 211 | func ShouldHandleDevice(device string) bool { 212 | normal := len(device) == 3 && (strings.HasPrefix(device, "sd") || strings.HasPrefix(device, "vd")) 213 | aws := len(device) >= 4 && strings.HasPrefix(device, "xvd") 214 | return normal || aws 215 | } 216 | -------------------------------------------------------------------------------- /funcs/du.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | "github.com/toolkits/sys" 7 | "log" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func DuMetrics() (L []*model.MetricValue) { 13 | paths := g.DuPaths() 14 | for _, path := range paths { 15 | out, err := sys.CmdOutNoLn("du", "-bs", path) 16 | if err != nil { 17 | log.Println("du -bs", path, "fail", err) 18 | continue 19 | } 20 | 21 | arr := strings.Fields(out) 22 | if len(arr) == 1 { 23 | continue 24 | } 25 | 26 | size, err := strconv.ParseUint(arr[0], 10, 64) 27 | if err != nil { 28 | log.Println("cannot parse du -bs", path, "output") 29 | continue 30 | } 31 | 32 | L = append(L, GaugeValue(g.DU_BS, size, "path="+path)) 33 | } 34 | 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /funcs/funcs.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | ) 7 | 8 | type FuncsAndInterval struct { 9 | Fs []func() []*model.MetricValue 10 | Interval int 11 | } 12 | 13 | var Mappers []FuncsAndInterval 14 | 15 | func BuildMappers() { 16 | interval := g.Config().Transfer.Interval 17 | Mappers = []FuncsAndInterval{ 18 | FuncsAndInterval{ 19 | Fs: []func() []*model.MetricValue{ 20 | AgentMetrics, 21 | CpuMetrics, 22 | NetMetrics, 23 | KernelMetrics, 24 | LoadAvgMetrics, 25 | MemMetrics, 26 | DiskIOMetrics, 27 | IOStatsMetrics, 28 | NetstatMetrics, 29 | ProcMetrics, 30 | UdpMetrics, 31 | }, 32 | Interval: interval, 33 | }, 34 | FuncsAndInterval{ 35 | Fs: []func() []*model.MetricValue{ 36 | DeviceMetrics, 37 | }, 38 | Interval: interval, 39 | }, 40 | FuncsAndInterval{ 41 | Fs: []func() []*model.MetricValue{ 42 | PortMetrics, 43 | SocketStatSummaryMetrics, 44 | }, 45 | Interval: interval, 46 | }, 47 | FuncsAndInterval{ 48 | Fs: []func() []*model.MetricValue{ 49 | DuMetrics, 50 | }, 51 | Interval: interval, 52 | }, 53 | FuncsAndInterval{ 54 | Fs: []func() []*model.MetricValue{ 55 | UrlMetrics, 56 | }, 57 | Interval: interval, 58 | }, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /funcs/ifstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | "github.com/toolkits/nux" 7 | "log" 8 | ) 9 | 10 | func NetMetrics() []*model.MetricValue { 11 | return CoreNetMetrics(g.Config().Collector.IfacePrefix) 12 | } 13 | 14 | func CoreNetMetrics(ifacePrefix []string) []*model.MetricValue { 15 | 16 | netIfs, err := nux.NetIfs(ifacePrefix) 17 | if err != nil { 18 | log.Println(err) 19 | return []*model.MetricValue{} 20 | } 21 | 22 | cnt := len(netIfs) 23 | ret := make([]*model.MetricValue, cnt*23) 24 | 25 | for idx, netIf := range netIfs { 26 | iface := "iface=" + netIf.Iface 27 | ret[idx*23+0] = CounterValue("net.if.in.bytes", netIf.InBytes, iface) 28 | ret[idx*23+1] = CounterValue("net.if.in.packets", netIf.InPackages, iface) 29 | ret[idx*23+2] = CounterValue("net.if.in.errors", netIf.InErrors, iface) 30 | ret[idx*23+3] = CounterValue("net.if.in.dropped", netIf.InDropped, iface) 31 | ret[idx*23+4] = CounterValue("net.if.in.fifo.errs", netIf.InFifoErrs, iface) 32 | ret[idx*23+5] = CounterValue("net.if.in.frame.errs", netIf.InFrameErrs, iface) 33 | ret[idx*23+6] = CounterValue("net.if.in.compressed", netIf.InCompressed, iface) 34 | ret[idx*23+7] = CounterValue("net.if.in.multicast", netIf.InMulticast, iface) 35 | ret[idx*23+8] = CounterValue("net.if.out.bytes", netIf.OutBytes, iface) 36 | ret[idx*23+9] = CounterValue("net.if.out.packets", netIf.OutPackages, iface) 37 | ret[idx*23+10] = CounterValue("net.if.out.errors", netIf.OutErrors, iface) 38 | ret[idx*23+11] = CounterValue("net.if.out.dropped", netIf.OutDropped, iface) 39 | ret[idx*23+12] = CounterValue("net.if.out.fifo.errs", netIf.OutFifoErrs, iface) 40 | ret[idx*23+13] = CounterValue("net.if.out.collisions", netIf.OutCollisions, iface) 41 | ret[idx*23+14] = CounterValue("net.if.out.carrier.errs", netIf.OutCarrierErrs, iface) 42 | ret[idx*23+15] = CounterValue("net.if.out.compressed", netIf.OutCompressed, iface) 43 | ret[idx*23+16] = CounterValue("net.if.total.bytes", netIf.TotalBytes, iface) 44 | ret[idx*23+17] = CounterValue("net.if.total.packets", netIf.TotalPackages, iface) 45 | ret[idx*23+18] = CounterValue("net.if.total.errors", netIf.TotalErrors, iface) 46 | ret[idx*23+19] = CounterValue("net.if.total.dropped", netIf.TotalDropped, iface) 47 | ret[idx*23+20] = GaugeValue("net.if.speed.bits", netIf.SpeedBits, iface) 48 | ret[idx*23+21] = CounterValue("net.if.in.percent", netIf.InPercent, iface) 49 | ret[idx*23+22] = CounterValue("net.if.out.percent", netIf.OutPercent, iface) 50 | } 51 | return ret 52 | } 53 | -------------------------------------------------------------------------------- /funcs/kernel.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | func KernelMetrics() (L []*model.MetricValue) { 10 | 11 | maxFiles, err := nux.KernelMaxFiles() 12 | if err != nil { 13 | log.Println(err) 14 | return 15 | } 16 | 17 | L = append(L, GaugeValue("kernel.maxfiles", maxFiles)) 18 | 19 | maxProc, err := nux.KernelMaxProc() 20 | if err != nil { 21 | log.Println(err) 22 | return 23 | } 24 | 25 | L = append(L, GaugeValue("kernel.maxproc", maxProc)) 26 | 27 | allocateFiles, err := nux.KernelAllocateFiles() 28 | if err != nil { 29 | log.Println(err) 30 | return 31 | } 32 | 33 | L = append(L, GaugeValue("kernel.files.allocated", allocateFiles)) 34 | L = append(L, GaugeValue("kernel.files.left", maxFiles-allocateFiles)) 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /funcs/loadavg.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | func LoadAvgMetrics() []*model.MetricValue { 10 | load, err := nux.LoadAvg() 11 | if err != nil { 12 | log.Println(err) 13 | return nil 14 | } 15 | 16 | return []*model.MetricValue{ 17 | GaugeValue("load.1min", load.Avg1min), 18 | GaugeValue("load.5min", load.Avg5min), 19 | GaugeValue("load.15min", load.Avg15min), 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /funcs/meminfo.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | func MemMetrics() []*model.MetricValue { 10 | m, err := nux.MemInfo() 11 | if err != nil { 12 | log.Println(err) 13 | return nil 14 | } 15 | 16 | memFree := m.MemFree + m.Buffers + m.Cached 17 | memUsed := m.MemTotal - memFree 18 | 19 | pmemFree := 0.0 20 | pmemUsed := 0.0 21 | if m.MemTotal != 0 { 22 | pmemFree = float64(memFree) * 100.0 / float64(m.MemTotal) 23 | pmemUsed = float64(memUsed) * 100.0 / float64(m.MemTotal) 24 | } 25 | 26 | pswapFree := 0.0 27 | pswapUsed := 0.0 28 | if m.SwapTotal != 0 { 29 | pswapFree = float64(m.SwapFree) * 100.0 / float64(m.SwapTotal) 30 | pswapUsed = float64(m.SwapUsed) * 100.0 / float64(m.SwapTotal) 31 | } 32 | 33 | return []*model.MetricValue{ 34 | GaugeValue("mem.memtotal", m.MemTotal), 35 | GaugeValue("mem.memused", memUsed), 36 | GaugeValue("mem.memfree", memFree), 37 | GaugeValue("mem.swaptotal", m.SwapTotal), 38 | GaugeValue("mem.swapused", m.SwapUsed), 39 | GaugeValue("mem.swapfree", m.SwapFree), 40 | GaugeValue("mem.memfree.percent", pmemFree), 41 | GaugeValue("mem.memused.percent", pmemUsed), 42 | GaugeValue("mem.swapfree.percent", pswapFree), 43 | GaugeValue("mem.swapused.percent", pswapUsed), 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /funcs/netstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | var USES = map[string]struct{}{ 10 | "PruneCalled": struct{}{}, 11 | "LockDroppedIcmps": struct{}{}, 12 | "ArpFilter": struct{}{}, 13 | "TW": struct{}{}, 14 | "DelayedACKLocked": struct{}{}, 15 | "ListenOverflows": struct{}{}, 16 | "ListenDrops": struct{}{}, 17 | "TCPPrequeueDropped": struct{}{}, 18 | "TCPTSReorder": struct{}{}, 19 | "TCPDSACKUndo": struct{}{}, 20 | "TCPLoss": struct{}{}, 21 | "TCPLostRetransmit": struct{}{}, 22 | "TCPLossFailures": struct{}{}, 23 | "TCPFastRetrans": struct{}{}, 24 | "TCPTimeouts": struct{}{}, 25 | "TCPSchedulerFailed": struct{}{}, 26 | "TCPAbortOnMemory": struct{}{}, 27 | "TCPAbortOnTimeout": struct{}{}, 28 | "TCPAbortFailed": struct{}{}, 29 | "TCPMemoryPressures": struct{}{}, 30 | "TCPSpuriousRTOs": struct{}{}, 31 | "TCPBacklogDrop": struct{}{}, 32 | "TCPMinTTLDrop": struct{}{}, 33 | } 34 | 35 | func NetstatMetrics() (L []*model.MetricValue) { 36 | tcpExts, err := nux.Netstat("TcpExt") 37 | 38 | if err != nil { 39 | log.Println(err) 40 | return 41 | } 42 | 43 | cnt := len(tcpExts) 44 | if cnt == 0 { 45 | return 46 | } 47 | 48 | for key, val := range tcpExts { 49 | if _, ok := USES[key]; !ok { 50 | continue 51 | } 52 | L = append(L, CounterValue("TcpExt."+key, val)) 53 | } 54 | 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /funcs/portstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/agent/g" 6 | "github.com/open-falcon/common/model" 7 | "github.com/toolkits/nux" 8 | "github.com/toolkits/slice" 9 | "log" 10 | ) 11 | 12 | func PortMetrics() (L []*model.MetricValue) { 13 | 14 | reportPorts := g.ReportPorts() 15 | sz := len(reportPorts) 16 | if sz == 0 { 17 | return 18 | } 19 | 20 | allListeningPorts, err := nux.ListeningPorts() 21 | if err != nil { 22 | log.Println(err) 23 | return 24 | } 25 | 26 | for i := 0; i < sz; i++ { 27 | tags := fmt.Sprintf("port=%d", reportPorts[i]) 28 | if slice.ContainsInt64(allListeningPorts, reportPorts[i]) { 29 | L = append(L, GaugeValue(g.NET_PORT_LISTEN, 1, tags)) 30 | } else { 31 | L = append(L, GaugeValue(g.NET_PORT_LISTEN, 0, tags)) 32 | } 33 | } 34 | 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /funcs/procs.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/open-falcon/common/model" 6 | "github.com/toolkits/nux" 7 | "log" 8 | "strings" 9 | ) 10 | 11 | func ProcMetrics() (L []*model.MetricValue) { 12 | 13 | reportProcs := g.ReportProcs() 14 | sz := len(reportProcs) 15 | if sz == 0 { 16 | return 17 | } 18 | 19 | ps, err := nux.AllProcs() 20 | if err != nil { 21 | log.Println(err) 22 | return 23 | } 24 | 25 | pslen := len(ps) 26 | 27 | for tags, m := range reportProcs { 28 | cnt := 0 29 | for i := 0; i < pslen; i++ { 30 | if is_a(ps[i], m) { 31 | cnt++ 32 | } 33 | } 34 | 35 | L = append(L, GaugeValue(g.PROC_NUM, cnt, tags)) 36 | } 37 | 38 | return 39 | } 40 | 41 | func is_a(p *nux.Proc, m map[int]string) bool { 42 | // only one kv pair 43 | for key, val := range m { 44 | if key == 1 { 45 | // name 46 | if val != p.Name { 47 | return false 48 | } 49 | } else if key == 2 { 50 | // cmdline 51 | if !strings.Contains(p.Cmdline, val) { 52 | return false 53 | } 54 | } 55 | } 56 | return true 57 | } 58 | -------------------------------------------------------------------------------- /funcs/snmp.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | func UdpMetrics() []*model.MetricValue { 10 | udp, err := nux.Snmp("Udp") 11 | if err != nil { 12 | log.Println("read snmp fail", err) 13 | return []*model.MetricValue{} 14 | } 15 | 16 | count := len(udp) 17 | ret := make([]*model.MetricValue, count) 18 | i := 0 19 | for key, val := range udp { 20 | ret[i] = CounterValue("snmp.Udp."+key, val) 21 | i++ 22 | } 23 | 24 | return ret 25 | } 26 | -------------------------------------------------------------------------------- /funcs/sockstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "github.com/open-falcon/common/model" 5 | "github.com/toolkits/nux" 6 | "log" 7 | ) 8 | 9 | func SocketStatSummaryMetrics() (L []*model.MetricValue) { 10 | ssMap, err := nux.SocketStatSummary() 11 | if err != nil { 12 | log.Println(err) 13 | return 14 | } 15 | 16 | for k, v := range ssMap { 17 | L = append(L, GaugeValue("ss."+k, v)) 18 | } 19 | 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /funcs/urlstat.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "log" 8 | "strings" 9 | 10 | "github.com/open-falcon/agent/g" 11 | "github.com/open-falcon/common/model" 12 | "github.com/toolkits/file" 13 | "github.com/toolkits/sys" 14 | ) 15 | 16 | func UrlMetrics() (L []*model.MetricValue) { 17 | reportUrls := g.ReportUrls() 18 | sz := len(reportUrls) 19 | if sz == 0 { 20 | return 21 | } 22 | hostname, err := g.Hostname() 23 | if err != nil { 24 | hostname = "None" 25 | } 26 | for furl, timeout := range reportUrls { 27 | tags := fmt.Sprintf("url=%v,timeout=%v,src=%v", furl, timeout, hostname) 28 | if ok, _ := probeUrl(furl, timeout); !ok { 29 | L = append(L, GaugeValue(g.URL_CHECK_HEALTH, 0, tags)) 30 | continue 31 | } 32 | L = append(L, GaugeValue(g.URL_CHECK_HEALTH, 1, tags)) 33 | } 34 | return 35 | } 36 | 37 | func probeUrl(furl string, timeout string) (bool, error) { 38 | bs, err := sys.CmdOutBytes("curl", "--max-filesize", "102400", "-I", "-m", timeout, "-o", "/dev/null", "-s", "-w", "%{http_code}", furl) 39 | if err != nil { 40 | log.Printf("probe url [%v] failed.the err is: [%v]\n", furl, err) 41 | return false, err 42 | } 43 | reader := bufio.NewReader(bytes.NewBuffer(bs)) 44 | retcode, err := file.ReadLine(reader) 45 | if err != nil { 46 | log.Println("read retcode failed.err is:", err) 47 | return false, err 48 | } 49 | if strings.TrimSpace(string(retcode)) != "200" { 50 | log.Printf("return code [%v] is not 200.query url is [%v]", string(retcode), furl) 51 | return false, err 52 | } 53 | return true, err 54 | } 55 | -------------------------------------------------------------------------------- /g/cfg.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | "sync" 8 | 9 | "github.com/toolkits/file" 10 | ) 11 | 12 | type PluginConfig struct { 13 | Enabled bool `json:"enabled"` 14 | Dir string `json:"dir"` 15 | Git string `json:"git"` 16 | LogDir string `json:"logs"` 17 | } 18 | 19 | type HeartbeatConfig struct { 20 | Enabled bool `json:"enabled"` 21 | Addr string `json:"addr"` 22 | Interval int `json:"interval"` 23 | Timeout int `json:"timeout"` 24 | } 25 | 26 | type TransferConfig struct { 27 | Enabled bool `json:"enabled"` 28 | Addrs []string `json:"addrs"` 29 | Interval int `json:"interval"` 30 | Timeout int `json:"timeout"` 31 | } 32 | 33 | type HttpConfig struct { 34 | Enabled bool `json:"enabled"` 35 | Listen string `json:"listen"` 36 | Backdoor bool `json:"backdoor"` 37 | } 38 | 39 | type CollectorConfig struct { 40 | IfacePrefix []string `json:"ifacePrefix"` 41 | } 42 | 43 | type GlobalConfig struct { 44 | Debug bool `json:"debug"` 45 | Hostname string `json:"hostname"` 46 | IP string `json:"ip"` 47 | Plugin *PluginConfig `json:"plugin"` 48 | Heartbeat *HeartbeatConfig `json:"heartbeat"` 49 | Transfer *TransferConfig `json:"transfer"` 50 | Http *HttpConfig `json:"http"` 51 | Collector *CollectorConfig `json:"collector"` 52 | IgnoreMetrics map[string]bool `json:"ignore"` 53 | } 54 | 55 | var ( 56 | ConfigFile string 57 | config *GlobalConfig 58 | lock = new(sync.RWMutex) 59 | ) 60 | 61 | func Config() *GlobalConfig { 62 | lock.RLock() 63 | defer lock.RUnlock() 64 | return config 65 | } 66 | 67 | func Hostname() (string, error) { 68 | hostname := Config().Hostname 69 | if hostname != "" { 70 | return hostname, nil 71 | } 72 | 73 | hostname, err := os.Hostname() 74 | if err != nil { 75 | log.Println("ERROR: os.Hostname() fail", err) 76 | } 77 | return hostname, err 78 | } 79 | 80 | func IP() string { 81 | ip := Config().IP 82 | if ip != "" { 83 | // use ip in configuration 84 | return ip 85 | } 86 | 87 | if len(LocalIp) > 0 { 88 | ip = LocalIp 89 | } 90 | 91 | return ip 92 | } 93 | 94 | func ParseConfig(cfg string) { 95 | if cfg == "" { 96 | log.Fatalln("use -c to specify configuration file") 97 | } 98 | 99 | if !file.IsExist(cfg) { 100 | log.Fatalln("config file:", cfg, "is not existent. maybe you need `mv cfg.example.json cfg.json`") 101 | } 102 | 103 | ConfigFile = cfg 104 | 105 | configContent, err := file.ToTrimString(cfg) 106 | if err != nil { 107 | log.Fatalln("read config file:", cfg, "fail:", err) 108 | } 109 | 110 | var c GlobalConfig 111 | err = json.Unmarshal([]byte(configContent), &c) 112 | if err != nil { 113 | log.Fatalln("parse config file:", cfg, "fail:", err) 114 | } 115 | 116 | lock.Lock() 117 | defer lock.Unlock() 118 | 119 | config = &c 120 | 121 | log.Println("read config file:", cfg, "successfully") 122 | } 123 | -------------------------------------------------------------------------------- /g/const.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // changelog: 8 | // 3.1.3: code refactor 9 | // 3.1.4: bugfix ignore configuration 10 | // 5.0.0: 支持通过配置控制是否开启/run接口;收集udp流量数据;du某个目录的大小 11 | // 5.1.0: 同步插件的时候不再使用checksum机制 12 | // 5.1.1: 修复往多个transfer发送数据的时候crash的问题 13 | const ( 14 | VERSION = "5.1.1" 15 | COLLECT_INTERVAL = time.Second 16 | URL_CHECK_HEALTH = "url.check.health" 17 | NET_PORT_LISTEN = "net.port.listen" 18 | DU_BS = "du.bs" 19 | PROC_NUM = "proc.num" 20 | ) 21 | -------------------------------------------------------------------------------- /g/g.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | ) 7 | 8 | func init() { 9 | runtime.GOMAXPROCS(runtime.NumCPU()) 10 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 11 | } 12 | -------------------------------------------------------------------------------- /g/rpc.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "github.com/toolkits/net" 5 | "log" 6 | "math" 7 | "net/rpc" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type SingleConnRpcClient struct { 13 | sync.Mutex 14 | rpcClient *rpc.Client 15 | RpcServer string 16 | Timeout time.Duration 17 | } 18 | 19 | func (this *SingleConnRpcClient) close() { 20 | if this.rpcClient != nil { 21 | this.rpcClient.Close() 22 | this.rpcClient = nil 23 | } 24 | } 25 | 26 | func (this *SingleConnRpcClient) serverConn() error { 27 | if this.rpcClient != nil { 28 | return nil 29 | } 30 | 31 | var err error 32 | var retry int = 1 33 | 34 | for { 35 | if this.rpcClient != nil { 36 | return nil 37 | } 38 | 39 | this.rpcClient, err = net.JsonRpcClient("tcp", this.RpcServer, this.Timeout) 40 | if err != nil { 41 | log.Printf("dial %s fail: %v", this.RpcServer, err) 42 | if retry > 3 { 43 | return err 44 | } 45 | time.Sleep(time.Duration(math.Pow(2.0, float64(retry))) * time.Second) 46 | retry++ 47 | continue 48 | } 49 | return err 50 | } 51 | } 52 | 53 | func (this *SingleConnRpcClient) Call(method string, args interface{}, reply interface{}) error { 54 | 55 | this.Lock() 56 | defer this.Unlock() 57 | 58 | err := this.serverConn() 59 | if err != nil { 60 | return err 61 | } 62 | 63 | timeout := time.Duration(50 * time.Second) 64 | done := make(chan error, 1) 65 | 66 | go func() { 67 | err := this.rpcClient.Call(method, args, reply) 68 | done <- err 69 | }() 70 | 71 | select { 72 | case <-time.After(timeout): 73 | log.Printf("[WARN] rpc call timeout %v => %v", this.rpcClient, this.RpcServer) 74 | this.close() 75 | case err := <-done: 76 | if err != nil { 77 | this.close() 78 | return err 79 | } 80 | } 81 | 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /g/tool.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/toolkits/file" 7 | "os/exec" 8 | "strings" 9 | ) 10 | 11 | func GetCurrPluginVersion() string { 12 | if !Config().Plugin.Enabled { 13 | return "plugin not enabled" 14 | } 15 | 16 | pluginDir := Config().Plugin.Dir 17 | if !file.IsExist(pluginDir) { 18 | return "plugin dir not existent" 19 | } 20 | 21 | cmd := exec.Command("git", "rev-parse", "HEAD") 22 | cmd.Dir = pluginDir 23 | 24 | var out bytes.Buffer 25 | cmd.Stdout = &out 26 | err := cmd.Run() 27 | if err != nil { 28 | return fmt.Sprintf("Error:%s", err.Error()) 29 | } 30 | 31 | return strings.TrimSpace(out.String()) 32 | } 33 | -------------------------------------------------------------------------------- /g/transfer.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | 9 | "github.com/open-falcon/common/model" 10 | ) 11 | 12 | var ( 13 | TransferClientsLock *sync.RWMutex = new(sync.RWMutex) 14 | TransferClients map[string]*SingleConnRpcClient = map[string]*SingleConnRpcClient{} 15 | ) 16 | 17 | func SendMetrics(metrics []*model.MetricValue, resp *model.TransferResponse) { 18 | rand.Seed(time.Now().UnixNano()) 19 | for _, i := range rand.Perm(len(Config().Transfer.Addrs)) { 20 | addr := Config().Transfer.Addrs[i] 21 | if _, ok := TransferClients[addr]; !ok { 22 | initTransferClient(addr) 23 | } 24 | if updateMetrics(addr, metrics, resp) { 25 | break 26 | } 27 | } 28 | } 29 | 30 | func initTransferClient(addr string) { 31 | TransferClientsLock.Lock() 32 | defer TransferClientsLock.Unlock() 33 | TransferClients[addr] = &SingleConnRpcClient{ 34 | RpcServer: addr, 35 | Timeout: time.Duration(Config().Transfer.Timeout) * time.Millisecond, 36 | } 37 | } 38 | 39 | func updateMetrics(addr string, metrics []*model.MetricValue, resp *model.TransferResponse) bool { 40 | TransferClientsLock.RLock() 41 | defer TransferClientsLock.RUnlock() 42 | err := TransferClients[addr].Call("Transfer.Update", metrics, resp) 43 | if err != nil { 44 | log.Println("call Transfer.Update fail", addr, err) 45 | return false 46 | } 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /g/var.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strings" 7 | "sync" 8 | "time" 9 | "net" 10 | "github.com/open-falcon/common/model" 11 | "github.com/toolkits/slice" 12 | ) 13 | 14 | var Root string 15 | 16 | func InitRootDir() { 17 | var err error 18 | Root, err = os.Getwd() 19 | if err != nil { 20 | log.Fatalln("getwd fail:", err) 21 | } 22 | } 23 | 24 | var LocalIp string 25 | 26 | func InitLocalIp() { 27 | if Config().Heartbeat.Enabled { 28 | conn, err := net.DialTimeout("tcp",Config().Heartbeat.Addr,time.Second*10) 29 | if err != nil { 30 | log.Println("get local addr failed !") 31 | }else{ 32 | LocalIp = strings.Split(conn.LocalAddr().String(),":")[0] 33 | conn.Close() 34 | } 35 | }else{ 36 | log.Println("hearbeat is not enabled, can't get localip") 37 | } 38 | } 39 | 40 | var ( 41 | HbsClient *SingleConnRpcClient 42 | ) 43 | 44 | func InitRpcClients() { 45 | if Config().Heartbeat.Enabled { 46 | HbsClient = &SingleConnRpcClient{ 47 | RpcServer: Config().Heartbeat.Addr, 48 | Timeout: time.Duration(Config().Heartbeat.Timeout) * time.Millisecond, 49 | } 50 | } 51 | } 52 | 53 | func SendToTransfer(metrics []*model.MetricValue) { 54 | if len(metrics) == 0 { 55 | return 56 | } 57 | 58 | debug := Config().Debug 59 | 60 | if debug { 61 | log.Printf("=> %v\n", len(metrics), metrics[0]) 62 | } 63 | 64 | var resp model.TransferResponse 65 | SendMetrics(metrics, &resp) 66 | 67 | if debug { 68 | log.Println("<=", &resp) 69 | } 70 | } 71 | 72 | var ( 73 | reportUrls map[string]string 74 | reportUrlsLock = new(sync.RWMutex) 75 | ) 76 | 77 | func ReportUrls() map[string]string { 78 | reportUrlsLock.RLock() 79 | defer reportUrlsLock.RUnlock() 80 | return reportUrls 81 | } 82 | 83 | func SetReportUrls(urls map[string]string) { 84 | reportUrlsLock.RLock() 85 | defer reportUrlsLock.RUnlock() 86 | reportUrls = urls 87 | } 88 | 89 | var ( 90 | reportPorts []int64 91 | reportPortsLock = new(sync.RWMutex) 92 | ) 93 | 94 | func ReportPorts() []int64 { 95 | reportPortsLock.RLock() 96 | defer reportPortsLock.RUnlock() 97 | return reportPorts 98 | } 99 | 100 | func SetReportPorts(ports []int64) { 101 | reportPortsLock.Lock() 102 | defer reportPortsLock.Unlock() 103 | reportPorts = ports 104 | } 105 | 106 | var ( 107 | duPaths []string 108 | duPathsLock = new(sync.RWMutex) 109 | ) 110 | 111 | func DuPaths() []string { 112 | duPathsLock.RLock() 113 | defer duPathsLock.RUnlock() 114 | return duPaths 115 | } 116 | 117 | func SetDuPaths(paths []string) { 118 | duPathsLock.Lock() 119 | defer duPathsLock.Unlock() 120 | duPaths = paths 121 | } 122 | 123 | var ( 124 | // tags => {1=>name, 2=>cmdline} 125 | // e.g. 'name=falcon-agent'=>{1=>falcon-agent} 126 | // e.g. 'cmdline=xx'=>{2=>xx} 127 | reportProcs map[string]map[int]string 128 | reportProcsLock = new(sync.RWMutex) 129 | ) 130 | 131 | func ReportProcs() map[string]map[int]string { 132 | reportProcsLock.RLock() 133 | defer reportProcsLock.RUnlock() 134 | return reportProcs 135 | } 136 | 137 | func SetReportProcs(procs map[string]map[int]string) { 138 | reportProcsLock.Lock() 139 | defer reportProcsLock.Unlock() 140 | reportProcs = procs 141 | } 142 | 143 | var ( 144 | ips []string 145 | ipsLock = new(sync.Mutex) 146 | ) 147 | 148 | func TrustableIps() []string { 149 | ipsLock.Lock() 150 | defer ipsLock.Unlock() 151 | return ips 152 | } 153 | 154 | func SetTrustableIps(ipStr string) { 155 | arr := strings.Split(ipStr, ",") 156 | ipsLock.Lock() 157 | defer ipsLock.Unlock() 158 | ips = arr 159 | } 160 | 161 | func IsTrustable(remoteAddr string) bool { 162 | ip := remoteAddr 163 | idx := strings.LastIndex(remoteAddr, ":") 164 | if idx > 0 { 165 | ip = remoteAddr[0:idx] 166 | } 167 | 168 | if ip == "127.0.0.1" { 169 | return true 170 | } 171 | 172 | return slice.ContainsString(TrustableIps(), ip) 173 | } 174 | -------------------------------------------------------------------------------- /http/admin.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/toolkits/file" 6 | "net/http" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func configAdminRoutes() { 12 | http.HandleFunc("/exit", func(w http.ResponseWriter, r *http.Request) { 13 | if g.IsTrustable(r.RemoteAddr) { 14 | w.Write([]byte("exiting...")) 15 | go func() { 16 | time.Sleep(time.Second) 17 | os.Exit(0) 18 | }() 19 | } else { 20 | w.Write([]byte("no privilege")) 21 | } 22 | }) 23 | 24 | http.HandleFunc("/config/reload", func(w http.ResponseWriter, r *http.Request) { 25 | if g.IsTrustable(r.RemoteAddr) { 26 | g.ParseConfig(g.ConfigFile) 27 | RenderDataJson(w, g.Config()) 28 | } else { 29 | w.Write([]byte("no privilege")) 30 | } 31 | }) 32 | 33 | http.HandleFunc("/workdir", func(w http.ResponseWriter, r *http.Request) { 34 | RenderDataJson(w, file.SelfDir()) 35 | }) 36 | 37 | http.HandleFunc("/ips", func(w http.ResponseWriter, r *http.Request) { 38 | RenderDataJson(w, g.TrustableIps()) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /http/cpu.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/agent/funcs" 6 | "github.com/toolkits/nux" 7 | "net/http" 8 | "runtime" 9 | ) 10 | 11 | func configCpuRoutes() { 12 | http.HandleFunc("/proc/cpu/num", func(w http.ResponseWriter, r *http.Request) { 13 | RenderDataJson(w, runtime.NumCPU()) 14 | }) 15 | 16 | http.HandleFunc("/proc/cpu/mhz", func(w http.ResponseWriter, r *http.Request) { 17 | data, err := nux.CpuMHz() 18 | AutoRender(w, data, err) 19 | }) 20 | 21 | http.HandleFunc("/page/cpu/usage", func(w http.ResponseWriter, r *http.Request) { 22 | if !funcs.CpuPrepared() { 23 | RenderMsgJson(w, "not prepared") 24 | return 25 | } 26 | 27 | idle := funcs.CpuIdle() 28 | busy := 100.0 - idle 29 | 30 | item := [10]string{ 31 | fmt.Sprintf("%.1f%%", idle), 32 | fmt.Sprintf("%.1f%%", busy), 33 | fmt.Sprintf("%.1f%%", funcs.CpuUser()), 34 | fmt.Sprintf("%.1f%%", funcs.CpuNice()), 35 | fmt.Sprintf("%.1f%%", funcs.CpuSystem()), 36 | fmt.Sprintf("%.1f%%", funcs.CpuIowait()), 37 | fmt.Sprintf("%.1f%%", funcs.CpuIrq()), 38 | fmt.Sprintf("%.1f%%", funcs.CpuSoftIrq()), 39 | fmt.Sprintf("%.1f%%", funcs.CpuSteal()), 40 | fmt.Sprintf("%.1f%%", funcs.CpuGuest()), 41 | } 42 | 43 | RenderDataJson(w, [][10]string{item}) 44 | }) 45 | 46 | http.HandleFunc("/proc/cpu/usage", func(w http.ResponseWriter, r *http.Request) { 47 | if !funcs.CpuPrepared() { 48 | RenderMsgJson(w, "not prepared") 49 | return 50 | } 51 | 52 | idle := funcs.CpuIdle() 53 | busy := 100.0 - idle 54 | 55 | RenderDataJson(w, map[string]interface{}{ 56 | "idle": idle, 57 | "busy": busy, 58 | "user": funcs.CpuUser(), 59 | "nice": funcs.CpuNice(), 60 | "system": funcs.CpuSystem(), 61 | "iowait": funcs.CpuIowait(), 62 | "irq": funcs.CpuIrq(), 63 | "softirq": funcs.CpuSoftIrq(), 64 | "steal": funcs.CpuSteal(), 65 | "guest": funcs.CpuGuest(), 66 | }) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /http/df.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/toolkits/core" 6 | "github.com/toolkits/nux" 7 | "net/http" 8 | ) 9 | 10 | func configDfRoutes() { 11 | http.HandleFunc("/page/df", func(w http.ResponseWriter, r *http.Request) { 12 | mountPoints, err := nux.ListMountPoint() 13 | if err != nil { 14 | RenderMsgJson(w, err.Error()) 15 | return 16 | } 17 | 18 | var ret [][]interface{} = make([][]interface{}, 0) 19 | for idx := range mountPoints { 20 | var du *nux.DeviceUsage 21 | du, err = nux.BuildDeviceUsage(mountPoints[idx][0], mountPoints[idx][1], mountPoints[idx][2]) 22 | if err == nil { 23 | ret = append(ret, 24 | []interface{}{ 25 | du.FsSpec, 26 | core.ReadableSize(float64(du.BlocksAll)), 27 | core.ReadableSize(float64(du.BlocksUsed)), 28 | core.ReadableSize(float64(du.BlocksFree)), 29 | fmt.Sprintf("%.1f%%", du.BlocksUsedPercent), 30 | du.FsFile, 31 | core.ReadableSize(float64(du.InodesAll)), 32 | core.ReadableSize(float64(du.InodesUsed)), 33 | core.ReadableSize(float64(du.InodesFree)), 34 | fmt.Sprintf("%.1f%%", du.InodesUsedPercent), 35 | du.FsVfstype, 36 | }) 37 | } 38 | } 39 | 40 | RenderDataJson(w, ret) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /http/health.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "net/http" 6 | ) 7 | 8 | func configHealthRoutes() { 9 | http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { 10 | w.Write([]byte("ok")) 11 | }) 12 | 13 | http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { 14 | w.Write([]byte(g.VERSION)) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/open-falcon/agent/g" 6 | "log" 7 | "net/http" 8 | _ "net/http/pprof" 9 | ) 10 | 11 | type Dto struct { 12 | Msg string `json:"msg"` 13 | Data interface{} `json:"data"` 14 | } 15 | 16 | func init() { 17 | configAdminRoutes() 18 | configCpuRoutes() 19 | configDfRoutes() 20 | configHealthRoutes() 21 | configIoStatRoutes() 22 | configKernelRoutes() 23 | configMemoryRoutes() 24 | configPageRoutes() 25 | configPluginRoutes() 26 | configPushRoutes() 27 | configRunRoutes() 28 | configSystemRoutes() 29 | } 30 | 31 | func RenderJson(w http.ResponseWriter, v interface{}) { 32 | bs, err := json.Marshal(v) 33 | if err != nil { 34 | http.Error(w, err.Error(), http.StatusInternalServerError) 35 | return 36 | } 37 | 38 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 39 | w.Write(bs) 40 | } 41 | 42 | func RenderDataJson(w http.ResponseWriter, data interface{}) { 43 | RenderJson(w, Dto{Msg: "success", Data: data}) 44 | } 45 | 46 | func RenderMsgJson(w http.ResponseWriter, msg string) { 47 | RenderJson(w, map[string]string{"msg": msg}) 48 | } 49 | 50 | func AutoRender(w http.ResponseWriter, data interface{}, err error) { 51 | if err != nil { 52 | RenderMsgJson(w, err.Error()) 53 | return 54 | } 55 | 56 | RenderDataJson(w, data) 57 | } 58 | 59 | func Start() { 60 | if !g.Config().Http.Enabled { 61 | return 62 | } 63 | 64 | addr := g.Config().Http.Listen 65 | if addr == "" { 66 | return 67 | } 68 | 69 | s := &http.Server{ 70 | Addr: addr, 71 | MaxHeaderBytes: 1 << 30, 72 | } 73 | 74 | log.Println("listening", addr) 75 | log.Fatalln(s.ListenAndServe()) 76 | } 77 | -------------------------------------------------------------------------------- /http/iostat.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/funcs" 5 | "net/http" 6 | ) 7 | 8 | func configIoStatRoutes() { 9 | http.HandleFunc("/page/diskio", func(w http.ResponseWriter, r *http.Request) { 10 | RenderDataJson(w, funcs.IOStatsForPage()) 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /http/kernel.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/toolkits/nux" 6 | "github.com/toolkits/sys" 7 | "net/http" 8 | ) 9 | 10 | func configKernelRoutes() { 11 | http.HandleFunc("/proc/kernel/hostname", func(w http.ResponseWriter, r *http.Request) { 12 | data, err := g.Hostname() 13 | AutoRender(w, data, err) 14 | }) 15 | 16 | http.HandleFunc("/proc/kernel/maxproc", func(w http.ResponseWriter, r *http.Request) { 17 | data, err := nux.KernelMaxProc() 18 | AutoRender(w, data, err) 19 | }) 20 | 21 | http.HandleFunc("/proc/kernel/maxfiles", func(w http.ResponseWriter, r *http.Request) { 22 | data, err := nux.KernelMaxFiles() 23 | AutoRender(w, data, err) 24 | }) 25 | 26 | http.HandleFunc("/proc/kernel/version", func(w http.ResponseWriter, r *http.Request) { 27 | data, err := sys.CmdOutNoLn("uname", "-r") 28 | AutoRender(w, data, err) 29 | }) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /http/memory.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/toolkits/nux" 5 | "net/http" 6 | ) 7 | 8 | func configMemoryRoutes() { 9 | http.HandleFunc("/page/memory", func(w http.ResponseWriter, r *http.Request) { 10 | mem, err := nux.MemInfo() 11 | if err != nil { 12 | RenderMsgJson(w, err.Error()) 13 | return 14 | } 15 | 16 | memFree := mem.MemFree + mem.Buffers + mem.Cached 17 | memUsed := mem.MemTotal - memFree 18 | var t uint64 = 1024 * 1024 19 | RenderDataJson(w, []interface{}{mem.MemTotal / t, memUsed / t, memFree / t}) 20 | }) 21 | 22 | http.HandleFunc("/proc/memory", func(w http.ResponseWriter, r *http.Request) { 23 | mem, err := nux.MemInfo() 24 | if err != nil { 25 | RenderMsgJson(w, err.Error()) 26 | return 27 | } 28 | 29 | memFree := mem.MemFree + mem.Buffers + mem.Cached 30 | memUsed := mem.MemTotal - memFree 31 | 32 | RenderDataJson(w, map[string]interface{}{ 33 | "total": mem.MemTotal, 34 | "free": memFree, 35 | "used": memUsed, 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /http/page.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/toolkits/file" 6 | "net/http" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func configPageRoutes() { 12 | 13 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 14 | if strings.HasSuffix(r.URL.Path, "/") { 15 | if !file.IsExist(filepath.Join(g.Root, "/public", r.URL.Path, "index.html")) { 16 | http.NotFound(w, r) 17 | return 18 | } 19 | } 20 | http.FileServer(http.Dir(filepath.Join(g.Root, "/public"))).ServeHTTP(w, r) 21 | }) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /http/plugin.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/open-falcon/agent/g" 6 | "github.com/open-falcon/agent/plugins" 7 | "github.com/toolkits/file" 8 | "net/http" 9 | "os/exec" 10 | ) 11 | 12 | func configPluginRoutes() { 13 | http.HandleFunc("/plugin/update", func(w http.ResponseWriter, r *http.Request) { 14 | if !g.Config().Plugin.Enabled { 15 | w.Write([]byte("plugin not enabled")) 16 | return 17 | } 18 | 19 | dir := g.Config().Plugin.Dir 20 | parentDir := file.Dir(dir) 21 | file.InsureDir(parentDir) 22 | 23 | if file.IsExist(dir) { 24 | // git pull 25 | cmd := exec.Command("git", "pull") 26 | cmd.Dir = dir 27 | err := cmd.Run() 28 | if err != nil { 29 | w.Write([]byte(fmt.Sprintf("git pull in dir:%s fail. error: %s", dir, err))) 30 | return 31 | } 32 | } else { 33 | // git clone 34 | cmd := exec.Command("git", "clone", g.Config().Plugin.Git, file.Basename(dir)) 35 | cmd.Dir = parentDir 36 | err := cmd.Run() 37 | if err != nil { 38 | w.Write([]byte(fmt.Sprintf("git clone in dir:%s fail. error: %s", parentDir, err))) 39 | return 40 | } 41 | } 42 | 43 | w.Write([]byte("success")) 44 | }) 45 | 46 | http.HandleFunc("/plugin/reset", func(w http.ResponseWriter, r *http.Request) { 47 | if !g.Config().Plugin.Enabled { 48 | w.Write([]byte("plugin not enabled")) 49 | return 50 | } 51 | 52 | dir := g.Config().Plugin.Dir 53 | 54 | if file.IsExist(dir) { 55 | cmd := exec.Command("git", "reset", "--hard") 56 | cmd.Dir = dir 57 | err := cmd.Run() 58 | if err != nil { 59 | w.Write([]byte(fmt.Sprintf("git reset --hard in dir:%s fail. error: %s", dir, err))) 60 | return 61 | } 62 | } 63 | w.Write([]byte("success")) 64 | }) 65 | 66 | http.HandleFunc("/plugins", func(w http.ResponseWriter, r *http.Request) { 67 | //TODO: not thread safe 68 | RenderDataJson(w, plugins.Plugins) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /http/push.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/open-falcon/agent/g" 6 | "github.com/open-falcon/common/model" 7 | "net/http" 8 | ) 9 | 10 | func configPushRoutes() { 11 | http.HandleFunc("/v1/push", func(w http.ResponseWriter, req *http.Request) { 12 | if req.ContentLength == 0 { 13 | http.Error(w, "body is blank", http.StatusBadRequest) 14 | return 15 | } 16 | 17 | decoder := json.NewDecoder(req.Body) 18 | var metrics []*model.MetricValue 19 | err := decoder.Decode(&metrics) 20 | if err != nil { 21 | http.Error(w, "connot decode body", http.StatusBadRequest) 22 | return 23 | } 24 | 25 | g.SendToTransfer(metrics) 26 | w.Write([]byte("success")) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /http/run.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/toolkits/sys" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | func configRunRoutes() { 11 | http.HandleFunc("/run", func(w http.ResponseWriter, r *http.Request) { 12 | if !g.Config().Http.Backdoor { 13 | w.Write([]byte("/run disabled")) 14 | return 15 | } 16 | 17 | if g.IsTrustable(r.RemoteAddr) { 18 | if r.ContentLength == 0 { 19 | http.Error(w, "body is blank", http.StatusBadRequest) 20 | return 21 | } 22 | 23 | bs, err := ioutil.ReadAll(r.Body) 24 | if err != nil { 25 | http.Error(w, err.Error(), http.StatusInternalServerError) 26 | return 27 | } 28 | 29 | body := string(bs) 30 | out, err := sys.CmdOutBytes("sh", "-c", body) 31 | if err != nil { 32 | w.Write([]byte("exec fail: " + err.Error())) 33 | return 34 | } 35 | 36 | w.Write(out) 37 | } else { 38 | w.Write([]byte("no privilege")) 39 | } 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /http/system.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/toolkits/nux" 6 | "net/http" 7 | "runtime" 8 | "time" 9 | ) 10 | 11 | func configSystemRoutes() { 12 | 13 | http.HandleFunc("/system/date", func(w http.ResponseWriter, req *http.Request) { 14 | RenderDataJson(w, time.Now().Format("2006-01-02 15:04:05")) 15 | }) 16 | 17 | http.HandleFunc("/page/system/uptime", func(w http.ResponseWriter, req *http.Request) { 18 | days, hours, mins, err := nux.SystemUptime() 19 | AutoRender(w, fmt.Sprintf("%d days %d hours %d minutes", days, hours, mins), err) 20 | }) 21 | 22 | http.HandleFunc("/proc/system/uptime", func(w http.ResponseWriter, req *http.Request) { 23 | days, hours, mins, err := nux.SystemUptime() 24 | if err != nil { 25 | RenderMsgJson(w, err.Error()) 26 | return 27 | } 28 | 29 | RenderDataJson(w, map[string]interface{}{ 30 | "days": days, 31 | "hours": hours, 32 | "mins": mins, 33 | }) 34 | }) 35 | 36 | http.HandleFunc("/page/system/loadavg", func(w http.ResponseWriter, req *http.Request) { 37 | cpuNum := runtime.NumCPU() 38 | load, err := nux.LoadAvg() 39 | if err != nil { 40 | RenderMsgJson(w, err.Error()) 41 | return 42 | } 43 | 44 | ret := [3][2]interface{}{ 45 | [2]interface{}{load.Avg1min, int64(load.Avg1min * 100.0 / float64(cpuNum))}, 46 | [2]interface{}{load.Avg5min, int64(load.Avg5min * 100.0 / float64(cpuNum))}, 47 | [2]interface{}{load.Avg15min, int64(load.Avg15min * 100.0 / float64(cpuNum))}, 48 | } 49 | RenderDataJson(w, ret) 50 | }) 51 | 52 | http.HandleFunc("/proc/system/loadavg", func(w http.ResponseWriter, req *http.Request) { 53 | data, err := nux.LoadAvg() 54 | AutoRender(w, data, err) 55 | }) 56 | 57 | } 58 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/open-falcon/agent/cron" 7 | "github.com/open-falcon/agent/funcs" 8 | "github.com/open-falcon/agent/g" 9 | "github.com/open-falcon/agent/http" 10 | "os" 11 | ) 12 | 13 | func main() { 14 | 15 | cfg := flag.String("c", "cfg.json", "configuration file") 16 | version := flag.Bool("v", false, "show version") 17 | check := flag.Bool("check", false, "check collector") 18 | 19 | flag.Parse() 20 | 21 | if *version { 22 | fmt.Println(g.VERSION) 23 | os.Exit(0) 24 | } 25 | 26 | if *check { 27 | funcs.CheckCollector() 28 | os.Exit(0) 29 | } 30 | 31 | g.ParseConfig(*cfg) 32 | 33 | g.InitRootDir() 34 | g.InitLocalIp() 35 | g.InitRpcClients() 36 | 37 | funcs.BuildMappers() 38 | 39 | go cron.InitDataHistory() 40 | 41 | cron.ReportAgentStatus() 42 | cron.SyncMinePlugins() 43 | cron.SyncBuiltinMetrics() 44 | cron.SyncTrustableIps() 45 | cron.Collect() 46 | 47 | go http.Start() 48 | 49 | select {} 50 | 51 | } 52 | -------------------------------------------------------------------------------- /plugins/plugins.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | type Plugin struct { 4 | FilePath string 5 | MTime int64 6 | Cycle int 7 | } 8 | 9 | var ( 10 | Plugins = make(map[string]*Plugin) 11 | PluginsWithScheduler = make(map[string]*PluginScheduler) 12 | ) 13 | 14 | func DelNoUsePlugins(newPlugins map[string]*Plugin) { 15 | for currKey, currPlugin := range Plugins { 16 | newPlugin, ok := newPlugins[currKey] 17 | if !ok || currPlugin.MTime != newPlugin.MTime { 18 | deletePlugin(currKey) 19 | } 20 | } 21 | } 22 | 23 | func AddNewPlugins(newPlugins map[string]*Plugin) { 24 | for fpath, newPlugin := range newPlugins { 25 | if _, ok := Plugins[fpath]; ok && newPlugin.MTime == Plugins[fpath].MTime { 26 | continue 27 | } 28 | 29 | Plugins[fpath] = newPlugin 30 | sch := NewPluginScheduler(newPlugin) 31 | PluginsWithScheduler[fpath] = sch 32 | sch.Schedule() 33 | } 34 | } 35 | 36 | func ClearAllPlugins() { 37 | for k := range Plugins { 38 | deletePlugin(k) 39 | } 40 | } 41 | 42 | func deletePlugin(key string) { 43 | v, ok := PluginsWithScheduler[key] 44 | if ok { 45 | v.Stop() 46 | delete(PluginsWithScheduler, key) 47 | } 48 | delete(Plugins, key) 49 | } 50 | -------------------------------------------------------------------------------- /plugins/reader.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "github.com/open-falcon/agent/g" 5 | "github.com/toolkits/file" 6 | "io/ioutil" 7 | "log" 8 | "path/filepath" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // key: sys/ntp/60_ntp.py 14 | func ListPlugins(relativePath string) map[string]*Plugin { 15 | ret := make(map[string]*Plugin) 16 | if relativePath == "" { 17 | return ret 18 | } 19 | 20 | dir := filepath.Join(g.Config().Plugin.Dir, relativePath) 21 | 22 | if !file.IsExist(dir) || file.IsFile(dir) { 23 | return ret 24 | } 25 | 26 | fs, err := ioutil.ReadDir(dir) 27 | if err != nil { 28 | log.Println("can not list files under", dir) 29 | return ret 30 | } 31 | 32 | for _, f := range fs { 33 | if f.IsDir() { 34 | continue 35 | } 36 | 37 | filename := f.Name() 38 | arr := strings.Split(filename, "_") 39 | if len(arr) < 2 { 40 | continue 41 | } 42 | 43 | // filename should be: $cycle_$xx 44 | var cycle int 45 | cycle, err = strconv.Atoi(arr[0]) 46 | if err != nil { 47 | continue 48 | } 49 | 50 | fpath := filepath.Join(relativePath, filename) 51 | plugin := &Plugin{FilePath: fpath, MTime: f.ModTime().Unix(), Cycle: cycle} 52 | ret[fpath] = plugin 53 | } 54 | 55 | return ret 56 | } 57 | -------------------------------------------------------------------------------- /plugins/scheduler.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "log" 7 | "os/exec" 8 | "path/filepath" 9 | "syscall" 10 | "time" 11 | 12 | "github.com/toolkits/file" 13 | "github.com/toolkits/sys" 14 | 15 | "github.com/open-falcon/agent/g" 16 | "github.com/open-falcon/common/model" 17 | ) 18 | 19 | type PluginScheduler struct { 20 | Ticker *time.Ticker 21 | Plugin *Plugin 22 | Quit chan struct{} 23 | } 24 | 25 | func NewPluginScheduler(p *Plugin) *PluginScheduler { 26 | scheduler := PluginScheduler{Plugin: p} 27 | scheduler.Ticker = time.NewTicker(time.Duration(p.Cycle) * time.Second) 28 | scheduler.Quit = make(chan struct{}) 29 | return &scheduler 30 | } 31 | 32 | func (this *PluginScheduler) Schedule() { 33 | go func() { 34 | for { 35 | select { 36 | case <-this.Ticker.C: 37 | PluginRun(this.Plugin) 38 | case <-this.Quit: 39 | this.Ticker.Stop() 40 | return 41 | } 42 | } 43 | }() 44 | } 45 | 46 | func (this *PluginScheduler) Stop() { 47 | close(this.Quit) 48 | } 49 | 50 | func PluginRun(plugin *Plugin) { 51 | 52 | timeout := plugin.Cycle*1000 - 500 53 | fpath := filepath.Join(g.Config().Plugin.Dir, plugin.FilePath) 54 | 55 | if !file.IsExist(fpath) { 56 | log.Println("no such plugin:", fpath) 57 | return 58 | } 59 | 60 | debug := g.Config().Debug 61 | if debug { 62 | log.Println(fpath, "running...") 63 | } 64 | 65 | cmd := exec.Command(fpath) 66 | var stdout bytes.Buffer 67 | cmd.Stdout = &stdout 68 | var stderr bytes.Buffer 69 | cmd.Stderr = &stderr 70 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 71 | cmd.Start() 72 | 73 | err, isTimeout := sys.CmdRunWithTimeout(cmd, time.Duration(timeout)*time.Millisecond) 74 | 75 | errStr := stderr.String() 76 | if errStr != "" { 77 | logFile := filepath.Join(g.Config().Plugin.LogDir, plugin.FilePath+".stderr.log") 78 | if _, err = file.WriteString(logFile, errStr); err != nil { 79 | log.Printf("[ERROR] write log to %s fail, error: %s\n", logFile, err) 80 | } 81 | } 82 | 83 | if isTimeout { 84 | // has be killed 85 | if err == nil && debug { 86 | log.Println("[INFO] timeout and kill process", fpath, "successfully") 87 | } 88 | 89 | if err != nil { 90 | log.Println("[ERROR] kill process", fpath, "occur error:", err) 91 | } 92 | 93 | return 94 | } 95 | 96 | if err != nil { 97 | log.Println("[ERROR] exec plugin", fpath, "fail. error:", err) 98 | return 99 | } 100 | 101 | // exec successfully 102 | data := stdout.Bytes() 103 | if len(data) == 0 { 104 | if debug { 105 | log.Println("[DEBUG] stdout of", fpath, "is blank") 106 | } 107 | return 108 | } 109 | 110 | var metrics []*model.MetricValue 111 | err = json.Unmarshal(data, &metrics) 112 | if err != nil { 113 | log.Printf("[ERROR] json.Unmarshal stdout of %s fail. error:%s stdout: \n%s\n", fpath, err, stdout.String()) 114 | return 115 | } 116 | 117 | g.SendToTransfer(metrics) 118 | } 119 | -------------------------------------------------------------------------------- /public/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} 2 | .clearfix:after{clear:both;} 3 | .hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} 4 | .input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} 5 | .hidden{display:none;visibility:hidden;} 6 | .visible-phone{display:none;} 7 | .visible-tablet{display:none;} 8 | .visible-desktop{display:block;} 9 | .hidden-phone{display:block;} 10 | .hidden-tablet{display:block;} 11 | .hidden-desktop{display:none;} 12 | @media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} -------------------------------------------------------------------------------- /public/css/font-awesome/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:'FontAwesome';src:url('../font/fontawesome-webfont.eot?v=3.2.1');src:url('../font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'),url('../font/fontawesome-webfont.woff?v=3.2.1') format('woff'),url('../font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'),url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg');font-weight:normal;font-style:normal;}[class^="icon-"],[class*=" icon-"]{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;} 2 | [class^="icon-"]:before,[class*=" icon-"]:before{text-decoration:inherit;display:inline-block;speak:none;} 3 | .icon-large:before{vertical-align:-10%;font-size:1.3333333333333333em;} 4 | a [class^="icon-"],a [class*=" icon-"]{display:inline;} 5 | [class^="icon-"].icon-fixed-width,[class*=" icon-"].icon-fixed-width{display:inline-block;width:1.1428571428571428em;text-align:right;padding-right:0.2857142857142857em;}[class^="icon-"].icon-fixed-width.icon-large,[class*=" icon-"].icon-fixed-width.icon-large{width:1.4285714285714286em;} 6 | .icons-ul{margin-left:2.142857142857143em;list-style-type:none;}.icons-ul>li{position:relative;} 7 | .icons-ul .icon-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;text-align:center;line-height:inherit;} 8 | [class^="icon-"].hide,[class*=" icon-"].hide{display:none;} 9 | .icon-muted{color:#eeeeee;} 10 | .icon-light{color:#ffffff;} 11 | .icon-dark{color:#333333;} 12 | .icon-border{border:solid 1px #eeeeee;padding:.2em .25em .15em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} 13 | .icon-2x{font-size:2em;}.icon-2x.icon-border{border-width:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} 14 | .icon-3x{font-size:3em;}.icon-3x.icon-border{border-width:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} 15 | .icon-4x{font-size:4em;}.icon-4x.icon-border{border-width:4px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} 16 | .icon-5x{font-size:5em;}.icon-5x.icon-border{border-width:5px;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px;} 17 | .pull-right{float:right;} 18 | .pull-left{float:left;} 19 | [class^="icon-"].pull-left,[class*=" icon-"].pull-left{margin-right:.3em;} 20 | [class^="icon-"].pull-right,[class*=" icon-"].pull-right{margin-left:.3em;} 21 | [class^="icon-"],[class*=" icon-"]{display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0;} 22 | .icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:none;} 23 | .btn [class^="icon-"].icon-large,.nav [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large,.nav [class*=" icon-"].icon-large{line-height:.9em;} 24 | .btn [class^="icon-"].icon-spin,.nav [class^="icon-"].icon-spin,.btn [class*=" icon-"].icon-spin,.nav [class*=" icon-"].icon-spin{display:inline-block;} 25 | .nav-tabs [class^="icon-"],.nav-pills [class^="icon-"],.nav-tabs [class*=" icon-"],.nav-pills [class*=" icon-"],.nav-tabs [class^="icon-"].icon-large,.nav-pills [class^="icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large{line-height:.9em;} 26 | .btn [class^="icon-"].pull-left.icon-2x,.btn [class*=" icon-"].pull-left.icon-2x,.btn [class^="icon-"].pull-right.icon-2x,.btn [class*=" icon-"].pull-right.icon-2x{margin-top:.18em;} 27 | .btn [class^="icon-"].icon-spin.icon-large,.btn [class*=" icon-"].icon-spin.icon-large{line-height:.8em;} 28 | .btn.btn-small [class^="icon-"].pull-left.icon-2x,.btn.btn-small [class*=" icon-"].pull-left.icon-2x,.btn.btn-small [class^="icon-"].pull-right.icon-2x,.btn.btn-small [class*=" icon-"].pull-right.icon-2x{margin-top:.25em;} 29 | .btn.btn-large [class^="icon-"],.btn.btn-large [class*=" icon-"]{margin-top:0;}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x,.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-top:.05em;} 30 | .btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x{margin-right:.2em;} 31 | .btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-left:.2em;} 32 | .nav-list [class^="icon-"],.nav-list [class*=" icon-"]{line-height:inherit;} 33 | .icon-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:-35%;}.icon-stack [class^="icon-"],.icon-stack [class*=" icon-"]{display:block;text-align:center;position:absolute;width:100%;height:100%;font-size:1em;line-height:inherit;*line-height:2em;} 34 | .icon-stack .icon-stack-base{font-size:2em;*line-height:1em;} 35 | .icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;} 36 | a .icon-stack,a .icon-spin{display:inline-block;text-decoration:none;} 37 | @-moz-keyframes spin{0%{-moz-transform:rotate(0deg);} 100%{-moz-transform:rotate(359deg);}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);} 100%{-webkit-transform:rotate(359deg);}}@-o-keyframes spin{0%{-o-transform:rotate(0deg);} 100%{-o-transform:rotate(359deg);}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg);} 100%{-ms-transform:rotate(359deg);}}@keyframes spin{0%{transform:rotate(0deg);} 100%{transform:rotate(359deg);}}.icon-rotate-90:before{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);} 38 | .icon-rotate-180:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);} 39 | .icon-rotate-270:before{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);} 40 | .icon-flip-horizontal:before{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1);} 41 | .icon-flip-vertical:before{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1);} 42 | a .icon-rotate-90:before,a .icon-rotate-180:before,a .icon-rotate-270:before,a .icon-flip-horizontal:before,a .icon-flip-vertical:before{display:inline-block;} 43 | .icon-glass:before{content:"\f000";} 44 | .icon-music:before{content:"\f001";} 45 | .icon-search:before{content:"\f002";} 46 | .icon-envelope-alt:before{content:"\f003";} 47 | .icon-heart:before{content:"\f004";} 48 | .icon-star:before{content:"\f005";} 49 | .icon-star-empty:before{content:"\f006";} 50 | .icon-user:before{content:"\f007";} 51 | .icon-film:before{content:"\f008";} 52 | .icon-th-large:before{content:"\f009";} 53 | .icon-th:before{content:"\f00a";} 54 | .icon-th-list:before{content:"\f00b";} 55 | .icon-ok:before{content:"\f00c";} 56 | .icon-remove:before{content:"\f00d";} 57 | .icon-zoom-in:before{content:"\f00e";} 58 | .icon-zoom-out:before{content:"\f010";} 59 | .icon-power-off:before,.icon-off:before{content:"\f011";} 60 | .icon-signal:before{content:"\f012";} 61 | .icon-gear:before,.icon-cog:before{content:"\f013";} 62 | .icon-trash:before{content:"\f014";} 63 | .icon-home:before{content:"\f015";} 64 | .icon-file-alt:before{content:"\f016";} 65 | .icon-time:before{content:"\f017";} 66 | .icon-road:before{content:"\f018";} 67 | .icon-download-alt:before{content:"\f019";} 68 | .icon-download:before{content:"\f01a";} 69 | .icon-upload:before{content:"\f01b";} 70 | .icon-inbox:before{content:"\f01c";} 71 | .icon-play-circle:before{content:"\f01d";} 72 | .icon-rotate-right:before,.icon-repeat:before{content:"\f01e";} 73 | .icon-refresh:before{content:"\f021";} 74 | .icon-list-alt:before{content:"\f022";} 75 | .icon-lock:before{content:"\f023";} 76 | .icon-flag:before{content:"\f024";} 77 | .icon-headphones:before{content:"\f025";} 78 | .icon-volume-off:before{content:"\f026";} 79 | .icon-volume-down:before{content:"\f027";} 80 | .icon-volume-up:before{content:"\f028";} 81 | .icon-qrcode:before{content:"\f029";} 82 | .icon-barcode:before{content:"\f02a";} 83 | .icon-tag:before{content:"\f02b";} 84 | .icon-tags:before{content:"\f02c";} 85 | .icon-book:before{content:"\f02d";} 86 | .icon-bookmark:before{content:"\f02e";} 87 | .icon-print:before{content:"\f02f";} 88 | .icon-camera:before{content:"\f030";} 89 | .icon-font:before{content:"\f031";} 90 | .icon-bold:before{content:"\f032";} 91 | .icon-italic:before{content:"\f033";} 92 | .icon-text-height:before{content:"\f034";} 93 | .icon-text-width:before{content:"\f035";} 94 | .icon-align-left:before{content:"\f036";} 95 | .icon-align-center:before{content:"\f037";} 96 | .icon-align-right:before{content:"\f038";} 97 | .icon-align-justify:before{content:"\f039";} 98 | .icon-list:before{content:"\f03a";} 99 | .icon-indent-left:before{content:"\f03b";} 100 | .icon-indent-right:before{content:"\f03c";} 101 | .icon-facetime-video:before{content:"\f03d";} 102 | .icon-picture:before{content:"\f03e";} 103 | .icon-pencil:before{content:"\f040";} 104 | .icon-map-marker:before{content:"\f041";} 105 | .icon-adjust:before{content:"\f042";} 106 | .icon-tint:before{content:"\f043";} 107 | .icon-edit:before{content:"\f044";} 108 | .icon-share:before{content:"\f045";} 109 | .icon-check:before{content:"\f046";} 110 | .icon-move:before{content:"\f047";} 111 | .icon-step-backward:before{content:"\f048";} 112 | .icon-fast-backward:before{content:"\f049";} 113 | .icon-backward:before{content:"\f04a";} 114 | .icon-play:before{content:"\f04b";} 115 | .icon-pause:before{content:"\f04c";} 116 | .icon-stop:before{content:"\f04d";} 117 | .icon-forward:before{content:"\f04e";} 118 | .icon-fast-forward:before{content:"\f050";} 119 | .icon-step-forward:before{content:"\f051";} 120 | .icon-eject:before{content:"\f052";} 121 | .icon-chevron-left:before{content:"\f053";} 122 | .icon-chevron-right:before{content:"\f054";} 123 | .icon-plus-sign:before{content:"\f055";} 124 | .icon-minus-sign:before{content:"\f056";} 125 | .icon-remove-sign:before{content:"\f057";} 126 | .icon-ok-sign:before{content:"\f058";} 127 | .icon-question-sign:before{content:"\f059";} 128 | .icon-info-sign:before{content:"\f05a";} 129 | .icon-screenshot:before{content:"\f05b";} 130 | .icon-remove-circle:before{content:"\f05c";} 131 | .icon-ok-circle:before{content:"\f05d";} 132 | .icon-ban-circle:before{content:"\f05e";} 133 | .icon-arrow-left:before{content:"\f060";} 134 | .icon-arrow-right:before{content:"\f061";} 135 | .icon-arrow-up:before{content:"\f062";} 136 | .icon-arrow-down:before{content:"\f063";} 137 | .icon-mail-forward:before,.icon-share-alt:before{content:"\f064";} 138 | .icon-resize-full:before{content:"\f065";} 139 | .icon-resize-small:before{content:"\f066";} 140 | .icon-plus:before{content:"\f067";} 141 | .icon-minus:before{content:"\f068";} 142 | .icon-asterisk:before{content:"\f069";} 143 | .icon-exclamation-sign:before{content:"\f06a";} 144 | .icon-gift:before{content:"\f06b";} 145 | .icon-leaf:before{content:"\f06c";} 146 | .icon-fire:before{content:"\f06d";} 147 | .icon-eye-open:before{content:"\f06e";} 148 | .icon-eye-close:before{content:"\f070";} 149 | .icon-warning-sign:before{content:"\f071";} 150 | .icon-plane:before{content:"\f072";} 151 | .icon-calendar:before{content:"\f073";} 152 | .icon-random:before{content:"\f074";} 153 | .icon-comment:before{content:"\f075";} 154 | .icon-magnet:before{content:"\f076";} 155 | .icon-chevron-up:before{content:"\f077";} 156 | .icon-chevron-down:before{content:"\f078";} 157 | .icon-retweet:before{content:"\f079";} 158 | .icon-shopping-cart:before{content:"\f07a";} 159 | .icon-folder-close:before{content:"\f07b";} 160 | .icon-folder-open:before{content:"\f07c";} 161 | .icon-resize-vertical:before{content:"\f07d";} 162 | .icon-resize-horizontal:before{content:"\f07e";} 163 | .icon-bar-chart:before{content:"\f080";} 164 | .icon-twitter-sign:before{content:"\f081";} 165 | .icon-facebook-sign:before{content:"\f082";} 166 | .icon-camera-retro:before{content:"\f083";} 167 | .icon-key:before{content:"\f084";} 168 | .icon-gears:before,.icon-cogs:before{content:"\f085";} 169 | .icon-comments:before{content:"\f086";} 170 | .icon-thumbs-up-alt:before{content:"\f087";} 171 | .icon-thumbs-down-alt:before{content:"\f088";} 172 | .icon-star-half:before{content:"\f089";} 173 | .icon-heart-empty:before{content:"\f08a";} 174 | .icon-signout:before{content:"\f08b";} 175 | .icon-linkedin-sign:before{content:"\f08c";} 176 | .icon-pushpin:before{content:"\f08d";} 177 | .icon-external-link:before{content:"\f08e";} 178 | .icon-signin:before{content:"\f090";} 179 | .icon-trophy:before{content:"\f091";} 180 | .icon-github-sign:before{content:"\f092";} 181 | .icon-upload-alt:before{content:"\f093";} 182 | .icon-lemon:before{content:"\f094";} 183 | .icon-phone:before{content:"\f095";} 184 | .icon-unchecked:before,.icon-check-empty:before{content:"\f096";} 185 | .icon-bookmark-empty:before{content:"\f097";} 186 | .icon-phone-sign:before{content:"\f098";} 187 | .icon-twitter:before{content:"\f099";} 188 | .icon-facebook:before{content:"\f09a";} 189 | .icon-github:before{content:"\f09b";} 190 | .icon-unlock:before{content:"\f09c";} 191 | .icon-credit-card:before{content:"\f09d";} 192 | .icon-rss:before{content:"\f09e";} 193 | .icon-hdd:before{content:"\f0a0";} 194 | .icon-bullhorn:before{content:"\f0a1";} 195 | .icon-bell:before{content:"\f0a2";} 196 | .icon-certificate:before{content:"\f0a3";} 197 | .icon-hand-right:before{content:"\f0a4";} 198 | .icon-hand-left:before{content:"\f0a5";} 199 | .icon-hand-up:before{content:"\f0a6";} 200 | .icon-hand-down:before{content:"\f0a7";} 201 | .icon-circle-arrow-left:before{content:"\f0a8";} 202 | .icon-circle-arrow-right:before{content:"\f0a9";} 203 | .icon-circle-arrow-up:before{content:"\f0aa";} 204 | .icon-circle-arrow-down:before{content:"\f0ab";} 205 | .icon-globe:before{content:"\f0ac";} 206 | .icon-wrench:before{content:"\f0ad";} 207 | .icon-tasks:before{content:"\f0ae";} 208 | .icon-filter:before{content:"\f0b0";} 209 | .icon-briefcase:before{content:"\f0b1";} 210 | .icon-fullscreen:before{content:"\f0b2";} 211 | .icon-group:before{content:"\f0c0";} 212 | .icon-link:before{content:"\f0c1";} 213 | .icon-cloud:before{content:"\f0c2";} 214 | .icon-beaker:before{content:"\f0c3";} 215 | .icon-cut:before{content:"\f0c4";} 216 | .icon-copy:before{content:"\f0c5";} 217 | .icon-paperclip:before,.icon-paper-clip:before{content:"\f0c6";} 218 | .icon-save:before{content:"\f0c7";} 219 | .icon-sign-blank:before{content:"\f0c8";} 220 | .icon-reorder:before{content:"\f0c9";} 221 | .icon-list-ul:before{content:"\f0ca";} 222 | .icon-list-ol:before{content:"\f0cb";} 223 | .icon-strikethrough:before{content:"\f0cc";} 224 | .icon-underline:before{content:"\f0cd";} 225 | .icon-table:before{content:"\f0ce";} 226 | .icon-magic:before{content:"\f0d0";} 227 | .icon-truck:before{content:"\f0d1";} 228 | .icon-pinterest:before{content:"\f0d2";} 229 | .icon-pinterest-sign:before{content:"\f0d3";} 230 | .icon-google-plus-sign:before{content:"\f0d4";} 231 | .icon-google-plus:before{content:"\f0d5";} 232 | .icon-money:before{content:"\f0d6";} 233 | .icon-caret-down:before{content:"\f0d7";} 234 | .icon-caret-up:before{content:"\f0d8";} 235 | .icon-caret-left:before{content:"\f0d9";} 236 | .icon-caret-right:before{content:"\f0da";} 237 | .icon-columns:before{content:"\f0db";} 238 | .icon-sort:before{content:"\f0dc";} 239 | .icon-sort-down:before{content:"\f0dd";} 240 | .icon-sort-up:before{content:"\f0de";} 241 | .icon-envelope:before{content:"\f0e0";} 242 | .icon-linkedin:before{content:"\f0e1";} 243 | .icon-rotate-left:before,.icon-undo:before{content:"\f0e2";} 244 | .icon-legal:before{content:"\f0e3";} 245 | .icon-dashboard:before{content:"\f0e4";} 246 | .icon-comment-alt:before{content:"\f0e5";} 247 | .icon-comments-alt:before{content:"\f0e6";} 248 | .icon-bolt:before{content:"\f0e7";} 249 | .icon-sitemap:before{content:"\f0e8";} 250 | .icon-umbrella:before{content:"\f0e9";} 251 | .icon-paste:before{content:"\f0ea";} 252 | .icon-lightbulb:before{content:"\f0eb";} 253 | .icon-exchange:before{content:"\f0ec";} 254 | .icon-cloud-download:before{content:"\f0ed";} 255 | .icon-cloud-upload:before{content:"\f0ee";} 256 | .icon-user-md:before{content:"\f0f0";} 257 | .icon-stethoscope:before{content:"\f0f1";} 258 | .icon-suitcase:before{content:"\f0f2";} 259 | .icon-bell-alt:before{content:"\f0f3";} 260 | .icon-coffee:before{content:"\f0f4";} 261 | .icon-food:before{content:"\f0f5";} 262 | .icon-file-text-alt:before{content:"\f0f6";} 263 | .icon-building:before{content:"\f0f7";} 264 | .icon-hospital:before{content:"\f0f8";} 265 | .icon-ambulance:before{content:"\f0f9";} 266 | .icon-medkit:before{content:"\f0fa";} 267 | .icon-fighter-jet:before{content:"\f0fb";} 268 | .icon-beer:before{content:"\f0fc";} 269 | .icon-h-sign:before{content:"\f0fd";} 270 | .icon-plus-sign-alt:before{content:"\f0fe";} 271 | .icon-double-angle-left:before{content:"\f100";} 272 | .icon-double-angle-right:before{content:"\f101";} 273 | .icon-double-angle-up:before{content:"\f102";} 274 | .icon-double-angle-down:before{content:"\f103";} 275 | .icon-angle-left:before{content:"\f104";} 276 | .icon-angle-right:before{content:"\f105";} 277 | .icon-angle-up:before{content:"\f106";} 278 | .icon-angle-down:before{content:"\f107";} 279 | .icon-desktop:before{content:"\f108";} 280 | .icon-laptop:before{content:"\f109";} 281 | .icon-tablet:before{content:"\f10a";} 282 | .icon-mobile-phone:before{content:"\f10b";} 283 | .icon-circle-blank:before{content:"\f10c";} 284 | .icon-quote-left:before{content:"\f10d";} 285 | .icon-quote-right:before{content:"\f10e";} 286 | .icon-spinner:before{content:"\f110";} 287 | .icon-circle:before{content:"\f111";} 288 | .icon-mail-reply:before,.icon-reply:before{content:"\f112";} 289 | .icon-github-alt:before{content:"\f113";} 290 | .icon-folder-close-alt:before{content:"\f114";} 291 | .icon-folder-open-alt:before{content:"\f115";} 292 | .icon-expand-alt:before{content:"\f116";} 293 | .icon-collapse-alt:before{content:"\f117";} 294 | .icon-smile:before{content:"\f118";} 295 | .icon-frown:before{content:"\f119";} 296 | .icon-meh:before{content:"\f11a";} 297 | .icon-gamepad:before{content:"\f11b";} 298 | .icon-keyboard:before{content:"\f11c";} 299 | .icon-flag-alt:before{content:"\f11d";} 300 | .icon-flag-checkered:before{content:"\f11e";} 301 | .icon-terminal:before{content:"\f120";} 302 | .icon-code:before{content:"\f121";} 303 | .icon-reply-all:before{content:"\f122";} 304 | .icon-mail-reply-all:before{content:"\f122";} 305 | .icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123";} 306 | .icon-location-arrow:before{content:"\f124";} 307 | .icon-crop:before{content:"\f125";} 308 | .icon-code-fork:before{content:"\f126";} 309 | .icon-unlink:before{content:"\f127";} 310 | .icon-question:before{content:"\f128";} 311 | .icon-info:before{content:"\f129";} 312 | .icon-exclamation:before{content:"\f12a";} 313 | .icon-superscript:before{content:"\f12b";} 314 | .icon-subscript:before{content:"\f12c";} 315 | .icon-eraser:before{content:"\f12d";} 316 | .icon-puzzle-piece:before{content:"\f12e";} 317 | .icon-microphone:before{content:"\f130";} 318 | .icon-microphone-off:before{content:"\f131";} 319 | .icon-shield:before{content:"\f132";} 320 | .icon-calendar-empty:before{content:"\f133";} 321 | .icon-fire-extinguisher:before{content:"\f134";} 322 | .icon-rocket:before{content:"\f135";} 323 | .icon-maxcdn:before{content:"\f136";} 324 | .icon-chevron-sign-left:before{content:"\f137";} 325 | .icon-chevron-sign-right:before{content:"\f138";} 326 | .icon-chevron-sign-up:before{content:"\f139";} 327 | .icon-chevron-sign-down:before{content:"\f13a";} 328 | .icon-html5:before{content:"\f13b";} 329 | .icon-css3:before{content:"\f13c";} 330 | .icon-anchor:before{content:"\f13d";} 331 | .icon-unlock-alt:before{content:"\f13e";} 332 | .icon-bullseye:before{content:"\f140";} 333 | .icon-ellipsis-horizontal:before{content:"\f141";} 334 | .icon-ellipsis-vertical:before{content:"\f142";} 335 | .icon-rss-sign:before{content:"\f143";} 336 | .icon-play-sign:before{content:"\f144";} 337 | .icon-ticket:before{content:"\f145";} 338 | .icon-minus-sign-alt:before{content:"\f146";} 339 | .icon-check-minus:before{content:"\f147";} 340 | .icon-level-up:before{content:"\f148";} 341 | .icon-level-down:before{content:"\f149";} 342 | .icon-check-sign:before{content:"\f14a";} 343 | .icon-edit-sign:before{content:"\f14b";} 344 | .icon-external-link-sign:before{content:"\f14c";} 345 | .icon-share-sign:before{content:"\f14d";} 346 | .icon-compass:before{content:"\f14e";} 347 | .icon-collapse:before{content:"\f150";} 348 | .icon-collapse-top:before{content:"\f151";} 349 | .icon-expand:before{content:"\f152";} 350 | .icon-euro:before,.icon-eur:before{content:"\f153";} 351 | .icon-gbp:before{content:"\f154";} 352 | .icon-dollar:before,.icon-usd:before{content:"\f155";} 353 | .icon-rupee:before,.icon-inr:before{content:"\f156";} 354 | .icon-yen:before,.icon-jpy:before{content:"\f157";} 355 | .icon-renminbi:before,.icon-cny:before{content:"\f158";} 356 | .icon-won:before,.icon-krw:before{content:"\f159";} 357 | .icon-bitcoin:before,.icon-btc:before{content:"\f15a";} 358 | .icon-file:before{content:"\f15b";} 359 | .icon-file-text:before{content:"\f15c";} 360 | .icon-sort-by-alphabet:before{content:"\f15d";} 361 | .icon-sort-by-alphabet-alt:before{content:"\f15e";} 362 | .icon-sort-by-attributes:before{content:"\f160";} 363 | .icon-sort-by-attributes-alt:before{content:"\f161";} 364 | .icon-sort-by-order:before{content:"\f162";} 365 | .icon-sort-by-order-alt:before{content:"\f163";} 366 | .icon-thumbs-up:before{content:"\f164";} 367 | .icon-thumbs-down:before{content:"\f165";} 368 | .icon-youtube-sign:before{content:"\f166";} 369 | .icon-youtube:before{content:"\f167";} 370 | .icon-xing:before{content:"\f168";} 371 | .icon-xing-sign:before{content:"\f169";} 372 | .icon-youtube-play:before{content:"\f16a";} 373 | .icon-dropbox:before{content:"\f16b";} 374 | .icon-stackexchange:before{content:"\f16c";} 375 | .icon-instagram:before{content:"\f16d";} 376 | .icon-flickr:before{content:"\f16e";} 377 | .icon-adn:before{content:"\f170";} 378 | .icon-bitbucket:before{content:"\f171";} 379 | .icon-bitbucket-sign:before{content:"\f172";} 380 | .icon-tumblr:before{content:"\f173";} 381 | .icon-tumblr-sign:before{content:"\f174";} 382 | .icon-long-arrow-down:before{content:"\f175";} 383 | .icon-long-arrow-up:before{content:"\f176";} 384 | .icon-long-arrow-left:before{content:"\f177";} 385 | .icon-long-arrow-right:before{content:"\f178";} 386 | .icon-apple:before{content:"\f179";} 387 | .icon-windows:before{content:"\f17a";} 388 | .icon-android:before{content:"\f17b";} 389 | .icon-linux:before{content:"\f17c";} 390 | .icon-dribbble:before{content:"\f17d";} 391 | .icon-skype:before{content:"\f17e";} 392 | .icon-foursquare:before{content:"\f180";} 393 | .icon-trello:before{content:"\f181";} 394 | .icon-female:before{content:"\f182";} 395 | .icon-male:before{content:"\f183";} 396 | .icon-gittip:before{content:"\f184";} 397 | .icon-sun:before{content:"\f185";} 398 | .icon-moon:before{content:"\f186";} 399 | .icon-archive:before{content:"\f187";} 400 | .icon-bug:before{content:"\f188";} 401 | .icon-vk:before{content:"\f189";} 402 | .icon-weibo:before{content:"\f18a";} 403 | .icon-renren:before{content:"\f18b";} 404 | -------------------------------------------------------------------------------- /public/css/font-awesome/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font-awesome/font/FontAwesome.otf -------------------------------------------------------------------------------- /public/css/font-awesome/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font-awesome/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/css/font-awesome/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font-awesome/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/css/font-awesome/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font-awesome/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/css/font/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /public/css/font/OpenSans-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font/OpenSans-Regular-webfont.ttf -------------------------------------------------------------------------------- /public/css/font/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/css/font/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /public/css/g.css: -------------------------------------------------------------------------------- 1 | *{ font:11pt Verdana,Arial,Microsoft YaHei,sans-serif; color:#111; word-wrap:break-word;} 2 | th {font-weight: bold;} 3 | h3.tit {font-weight: bold; border-left: 3px solid #FF8000; padding-left: 5px;} 4 | .mt0{ margin-top:0px!important;} 5 | .mt5{ margin-top:5px!important;} 6 | .mt10{ margin-top:10px!important;} 7 | .m10{ margin:10px!important;} 8 | .mt15{ margin-top:15px!important;} 9 | .mt20{ margin-top:20px!important;} 10 | .mb0{ margin-bottom:0px!important;} 11 | .mb5{ margin-bottom:5px!important;} 12 | .mb10{ margin-bottom:10px!important;} 13 | .mb15{ margin-bottom:15px!important;} 14 | .mb20{ margin-bottom:20px!important;} 15 | /*float*/ 16 | .fl{ float:left!important;} 17 | .fr{ float:right!important;} 18 | .fn{ float:none!important;} 19 | .pagination{display:inline-block;padding-left:0;margin:18px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.428571429;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#eee}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:17px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px} 20 | hr { 21 | height: 0; 22 | -moz-box-sizing: content-box; 23 | box-sizing: content-box; 24 | margin-top: 20px; 25 | margin-bottom: 20px; 26 | border: 0; 27 | border-top: 1px solid #eee; 28 | } 29 | -------------------------------------------------------------------------------- /public/css/odometer.css: -------------------------------------------------------------------------------- 1 | .odometer.odometer-auto-theme, .odometer.odometer-theme-default { 2 | display: -moz-inline-box; 3 | -moz-box-orient: vertical; 4 | display: inline-block; 5 | vertical-align: middle; 6 | *vertical-align: auto; 7 | position: relative; 8 | } 9 | .odometer.odometer-auto-theme, .odometer.odometer-theme-default { 10 | *display: inline; 11 | } 12 | .odometer.odometer-auto-theme .odometer-digit, .odometer.odometer-theme-default .odometer-digit { 13 | display: -moz-inline-box; 14 | -moz-box-orient: vertical; 15 | display: inline-block; 16 | vertical-align: middle; 17 | *vertical-align: auto; 18 | position: relative; 19 | } 20 | .odometer.odometer-auto-theme .odometer-digit, .odometer.odometer-theme-default .odometer-digit { 21 | *display: inline; 22 | } 23 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, .odometer.odometer-theme-default .odometer-digit .odometer-digit-spacer { 24 | display: -moz-inline-box; 25 | -moz-box-orient: vertical; 26 | display: inline-block; 27 | vertical-align: middle; 28 | *vertical-align: auto; 29 | visibility: hidden; 30 | } 31 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-spacer, .odometer.odometer-theme-default .odometer-digit .odometer-digit-spacer { 32 | *display: inline; 33 | } 34 | .odometer.odometer-auto-theme .odometer-digit .odometer-digit-inner, .odometer.odometer-theme-default .odometer-digit .odometer-digit-inner { 35 | text-align: left; 36 | display: block; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | right: 0; 41 | bottom: 0; 42 | overflow: hidden; 43 | } 44 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon, .odometer.odometer-theme-default .odometer-digit .odometer-ribbon { 45 | display: block; 46 | } 47 | .odometer.odometer-auto-theme .odometer-digit .odometer-ribbon-inner, .odometer.odometer-theme-default .odometer-digit .odometer-ribbon-inner { 48 | display: block; 49 | } 50 | .odometer.odometer-auto-theme .odometer-digit .odometer-value, .odometer.odometer-theme-default .odometer-digit .odometer-value { 51 | display: block; 52 | } 53 | .odometer.odometer-auto-theme .odometer-digit .odometer-value.odometer-last-value, .odometer.odometer-theme-default .odometer-digit .odometer-value.odometer-last-value { 54 | position: absolute; 55 | } 56 | .odometer.odometer-auto-theme.odometer-animating-up .odometer-ribbon-inner, .odometer.odometer-theme-default.odometer-animating-up .odometer-ribbon-inner { 57 | -webkit-transition: -webkit-transform 1s; 58 | -moz-transition: -moz-transform 1s; 59 | -ms-transition: -ms-transform 1s; 60 | -o-transition: -o-transform 1s; 61 | transition: transform 1s; 62 | } 63 | .odometer.odometer-auto-theme.odometer-animating-up.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-default.odometer-animating-up.odometer-animating .odometer-ribbon-inner { 64 | -webkit-transform: translateY(-100%); 65 | -moz-transform: translateY(-100%); 66 | -ms-transform: translateY(-100%); 67 | -o-transform: translateY(-100%); 68 | transform: translateY(-100%); 69 | } 70 | .odometer.odometer-auto-theme.odometer-animating-down .odometer-ribbon-inner, .odometer.odometer-theme-default.odometer-animating-down .odometer-ribbon-inner { 71 | -webkit-transform: translateY(-100%); 72 | -moz-transform: translateY(-100%); 73 | -ms-transform: translateY(-100%); 74 | -o-transform: translateY(-100%); 75 | transform: translateY(-100%); 76 | } 77 | .odometer.odometer-auto-theme.odometer-animating-down.odometer-animating .odometer-ribbon-inner, .odometer.odometer-theme-default.odometer-animating-down.odometer-animating .odometer-ribbon-inner { 78 | -webkit-transition: -webkit-transform 1s; 79 | -moz-transition: -moz-transform 1s; 80 | -ms-transition: -ms-transform 1s; 81 | -o-transition: -o-transform 1s; 82 | -o-transition: -o-transform 1s; 83 | transition: transform 1s; 84 | -webkit-transform: translateY(0); 85 | -moz-transform: translateY(0); 86 | -ms-transform: translateY(0); 87 | -o-transform: translateY(0); 88 | transform: translateY(0); 89 | } 90 | 91 | .odometer.odometer-auto-theme, .odometer.odometer-theme-default { 92 | font-family: "Helvetica Neue", sans-serif; 93 | line-height: 1.1em; 94 | } 95 | -------------------------------------------------------------------------------- /public/css/pages/dashboard.css: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------ 2 | Bootstrap Admin Template by EGrappler.com 3 | ------------------------------------------------------------------*/ 4 | 5 | 6 | 7 | /*------------------------------------------------------------------ 8 | [1. Shortcuts / .shortcuts] 9 | */ 10 | 11 | .shortcuts { 12 | text-align: center; 13 | } 14 | 15 | .shortcuts .shortcut { 16 | width: 22.50%; 17 | display: inline-block; 18 | padding: 12px 0; 19 | margin: 0 .9% 1em; 20 | vertical-align: top; 21 | 22 | text-decoration: none; 23 | 24 | background: #f9f6f1; 25 | 26 | border-radius: 5px; 27 | } 28 | 29 | .shortcuts .shortcut .shortcut-icon { 30 | margin-top: .25em; 31 | margin-bottom: .25em; 32 | 33 | font-size: 32px; 34 | color: #545454; 35 | } 36 | 37 | .shortcuts .shortcut:hover { 38 | background: #00ba8b; 39 | } 40 | 41 | .shortcuts .shortcut:hover span{ 42 | color: #fff; 43 | } 44 | 45 | .shortcuts .shortcut:hover .shortcut-icon { 46 | color: #fff; 47 | } 48 | 49 | .shortcuts .shortcut-label { 50 | display: block; 51 | 52 | font-weight: 400; 53 | color: #545454; 54 | } 55 | 56 | 57 | 58 | /*------------------------------------------------------------------ 59 | [2. Stats / .stats] 60 | */ 61 | 62 | .stats { 63 | width: 100%; 64 | display: table; 65 | padding: 0 0 0 10px; 66 | margin-top: .5em; 67 | margin-bottom: 1.9em; 68 | } 69 | 70 | .stats .stat { 71 | display: table-cell; 72 | width: 40%; 73 | vertical-align: top; 74 | 75 | font-size: 11px; 76 | font-weight: bold; 77 | color: #999; 78 | } 79 | 80 | .stat-value { 81 | display: block; 82 | margin-bottom: .55em; 83 | 84 | font-size: 30px; 85 | font-weight: bold; 86 | letter-spacing: -2px; 87 | color: #444; 88 | } 89 | 90 | .stat-time { 91 | text-align: center; 92 | padding-top: 1.5em; 93 | } 94 | 95 | .stat-time .stat-value { 96 | color: #19bc9c; 97 | font-size: 40px; 98 | } 99 | 100 | .stats #donut-chart { 101 | height: 100px; 102 | margin-left: -20px; 103 | } 104 | 105 | 106 | 107 | 108 | 109 | /*------------------------------------------------------------------ 110 | [3. News Item / .news-items] 111 | */ 112 | 113 | .news-items { 114 | margin: 1em 0 0; 115 | } 116 | 117 | .news-items li { 118 | display: table; 119 | padding: 0 2em 0 1.5em; 120 | padding-bottom: 1em; 121 | margin-bottom: 1em; 122 | border-bottom: 1px dotted #CCC; 123 | } 124 | 125 | .news-items li:last-child { padding-bottom: 0; border: none; } 126 | 127 | .news-item-date { 128 | display: table-cell; 129 | } 130 | 131 | .news-item-detail { 132 | display: table-cell; 133 | } 134 | 135 | .news-item-title { 136 | font-size: 13px; 137 | font-weight: 600; 138 | } 139 | 140 | .news-item-date { 141 | width: 75px; 142 | vertical-align: middle; 143 | text-align: center; 144 | 145 | } 146 | 147 | .news-item-day { 148 | display: block; 149 | margin-bottom: .25em; 150 | 151 | font-size: 24px; 152 | color: #888; 153 | } 154 | 155 | .news-item-preview { 156 | margin-bottom: 0; 157 | 158 | color: #777; 159 | } 160 | 161 | .news-item-month { 162 | display: block; 163 | padding-right: 1px; 164 | 165 | font-size: 12px; 166 | font-weight: 600; 167 | color: #888; 168 | } 169 | 170 | 171 | 172 | /*------------------------------------------------------------------ 173 | [4. Action Table / .action-table] 174 | */ 175 | 176 | .action-table .btn-small { 177 | padding: 4px 5px 5px; 178 | 179 | font-size: 10px; 180 | } 181 | 182 | .action-table .td-actions { 183 | width: 80px; 184 | 185 | text-align: center; 186 | } 187 | 188 | .action-table .td-actions .btn { 189 | margin-right: .5em; 190 | } 191 | 192 | .action-table .td-actions .btn:last-child { 193 | margin-rigth: 0; 194 | } 195 | 196 | 197 | 198 | .big_stats { 199 | width: 100%; 200 | display: table; 201 | margin-top: 1.5em; 202 | } 203 | 204 | .big-stats-container .widget-content { 205 | border:0; 206 | } 207 | 208 | .big_stats .stat { 209 | width: 35%; 210 | height: auto; 211 | text-align: center; 212 | display: table-cell; 213 | padding: 0 0 1em; 214 | position: relative; 215 | 216 | border-right: 1px solid #CCC; 217 | border-left: 1px solid #FFF; 218 | } 219 | 220 | .big_stats i { font-size:20px; display:block; line-height: 30px; color:#b2afaa; } 221 | .big_stats .stat i { font: 20px/2em "Open Sans", sans-serif; color:#19bc9c; } 222 | 223 | h6.bigstats { 224 | margin: 20px; 225 | border-bottom: 1px solid #eee; 226 | padding-bottom: 20px; 227 | margin-bottom: 26px; 228 | } 229 | 230 | .big_stats .stat:first-child { 231 | border-left: none; 232 | width:33%; 233 | } 234 | 235 | .big_stats .stat:last-child { 236 | border-right: none; 237 | width:33%; 238 | } 239 | 240 | .big_stats .stat h4 { 241 | font-size: 11px; 242 | font-weight: bold; 243 | color: #777; 244 | margin-bottom: 1.5em; 245 | } 246 | 247 | .big_stats .stat .value { 248 | font-size: 20px; 249 | font-weight: bold; 250 | color: #545454; 251 | line-height: 1em; 252 | } 253 | 254 | 255 | 256 | @media all and (max-width: 950px) and (min-width: 1px) { 257 | 258 | .big_stats { 259 | display: block; 260 | margin-bottom: -10px; 261 | } 262 | 263 | .big_stats .stat { 264 | width: 32%; 265 | display: block; 266 | margin-bottom: 1em; 267 | float: left; 268 | } 269 | 270 | 271 | 272 | } 273 | 274 | @media (max-width: 767px) { 275 | .big_stats .stat .value { 276 | font-size: 25px; 277 | } 278 | } 279 | 280 | 281 | 282 | 283 | @media (max-width: 979px) { 284 | 285 | .shortcuts .shortcut { 286 | width: 31%; 287 | } 288 | } 289 | 290 | 291 | @media (max-width: 480px) { 292 | 293 | .stats .stat { 294 | 295 | margin-bottom: 3em; 296 | } 297 | 298 | .stats .stat .stat-value { 299 | margin-bottom: .15em; 300 | 301 | font-size: 20px; 302 | } 303 | 304 | .stats { 305 | float: left; 306 | 307 | display: block; 308 | 309 | margin-bottom: 0; 310 | } 311 | 312 | #chart-stats { 313 | margin: 2em 0 1em; 314 | } 315 | 316 | .shortcuts .shortcut { 317 | width: 48%; 318 | } 319 | } 320 | 321 | /* ADDED BY AFAQ */ 322 | .widget-content { padding:0px; } 323 | .dataTables_length { display:none; } 324 | .dataTable { margin-bottom:0px; } 325 | 326 | .pulse { 327 | background:#19bc9c; 328 | border:1px solid #19bc9c; 329 | color:white; 330 | } 331 | 332 | .pulse-border { 333 | border-color:#19bc9c; 334 | } 335 | 336 | .navbar { 337 | position:fixed; 338 | top:0; 339 | z-index:9999999; 340 | width:100%; 341 | } 342 | 343 | .subnavbar { 344 | position:fixed; 345 | top:50px; 346 | z-index:9999999; 347 | width:100%; 348 | } 349 | 350 | .main { 351 | margin-top:130px; 352 | } 353 | .dataTables_paginate{ width:100%; text-align:center; margin-top:10px; margin-left:-30px;} 354 | .dataTables_paginate a{ margin-left:8px;} 355 | .dataTables_paginate a:hover{ pointer:pointer;} 356 | .navbar-fixed-top{ margin-left:0px;} 357 | .widget-search{display: inline-block; vertical-align: middle; margin: 0 6px; width: 200px;} 358 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------ 2 | Bootstrap Admin Template by EGrappler.com 3 | ------------------------------------------------------------------*/ 4 | 5 | 6 | 7 | /*------------------------------------------------------------------ 8 | [1. Global] 9 | */ 10 | 11 | @font-face { 12 | font-family: "Open Sans"; 13 | font-style: normal; 14 | font-weight: normal; 15 | src: url("font/OpenSans-Regular-webfont.eot") format("embedded-opentype"), 16 | url("font/OpenSans-Regular-webfont.woff") format("woff"), 17 | url("font/OpenSans-Regular-webfont.ttf") format("truetype"), 18 | url("font/OpenSans-Regular-webfont.svg#OpenSansRegular") format("svg"); 19 | } 20 | 21 | body { 22 | background: #f9f6f1; 23 | font: 12px/1.5em "Open Sans", sans-serif; 24 | } 25 | 26 | a { 27 | cursor: pointer; 28 | } 29 | 30 | input, 31 | button, 32 | select, 33 | textarea { 34 | font-family: 'Open Sans'; 35 | } 36 | 37 | .dropdown .dropdown-menu { 38 | -webkit-border-radius: 6px; 39 | -moz-border-radius: 6px; 40 | border-radius: 6px; 41 | } 42 | 43 | .btn-icon-only { 44 | padding-right: 3px; 45 | padding-left: 3px; 46 | } 47 | 48 | .table td { 49 | vertical-align: middle; 50 | } 51 | 52 | .table-bordered th { 53 | background: #E9E9E9; 54 | background:-moz-linear-gradient(top, #FAFAFA 0%, #E9E9E9 100%); /* FF3.6+ */ 55 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#FAFAFA), color-stop(100%,#E9E9E9)); /* Chrome,Safari4+ */ 56 | background:-webkit-linear-gradient(top, #FAFAFA 0%,#E9E9E9 100%); /* Chrome10+,Safari5.1+ */ 57 | background:-o-linear-gradient(top, #FAFAFA 0%,#E9E9E9 100%); /* Opera11.10+ */ 58 | background:-ms-linear-gradient(top, #FAFAFA 0%,#E9E9E9 100%); /* IE10+ */ 59 | background:linear-gradient(top, #FAFAFA 0%,#E9E9E9 100%); /* W3C */ 60 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FAFAFA', endColorstr='#E9E9E9'); 61 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#FAFAFA', endColorstr='#E9E9E9')"; 62 | 63 | font-size: 10px; 64 | color: #444; 65 | text-transform: uppercase; 66 | } 67 | 68 | 69 | /*------------------------------------------------------------------ 70 | [2. Navbar / .navbar] 71 | */ 72 | 73 | .navbar .container { 74 | position: relative; 75 | } 76 | 77 | .navbar-inner { 78 | padding: 7px 0; 79 | 80 | background: #00ba8b !important; 81 | 82 | -moz-border-radius: 0; 83 | -webkit-border-radius: 0; 84 | border-radius: 0; 85 | } 86 | 87 | .navbar-fixed-top { 88 | position: static; 89 | } 90 | 91 | .navbar .nav a { 92 | font-size: 11px; 93 | } 94 | .navbar .nav>li>a { color:#fff !important;} 95 | .navbar .brand { 96 | font-weight: 600; 97 | position: relative; 98 | top: 2px; 99 | } 100 | 101 | .navbar .search-query { 102 | background-color: #444; 103 | width: 150px; 104 | font-size: 11px; 105 | font-weight: bold; 106 | } 107 | 108 | .navbar .search-query::-webkit-input-placeholder { 109 | color: #666; 110 | } 111 | 112 | .navbar .search-query:-moz-placeholder { 113 | color: #666; 114 | } 115 | 116 | .navbar-search .search-query { background:#008866; border:0; color:#fff; line-height:normal;} 117 | 118 | 119 | /*------------------------------------------------------------------ 120 | [3. Subnavbar / .subnavbar] 121 | */ 122 | 123 | .subnavbar { 124 | margin-bottom: 2.5em; 125 | } 126 | 127 | .subnavbar-inner { 128 | height: 60px; 129 | background: #fff; 130 | border-bottom: 1px solid #d6d6d6; 131 | } 132 | 133 | .subnavbar .container > ul { 134 | display: inline-block; 135 | 136 | height: 80px; 137 | padding: 0; 138 | margin: 0; 139 | 140 | } 141 | 142 | .subnavbar .container > ul > li { 143 | float: left; 144 | 145 | min-width: 90px; 146 | height: 60px; 147 | padding: 0; 148 | margin: 0; 149 | 150 | text-align: center; 151 | list-style: none; 152 | 153 | border-left: 1px solid #d9d9d9; 154 | 155 | 156 | } 157 | 158 | .subnavbar .container > ul > li > a { 159 | display: block; 160 | 161 | height: 100%; 162 | padding: 0 15px; 163 | 164 | font-size: 12px; 165 | font-weight: bold; 166 | color: #b2afaa; 167 | } 168 | 169 | .subnavbar .container > ul > li > a:hover { 170 | color: #888; 171 | text-decoration: none; 172 | } 173 | 174 | .subnavbar .container > ul > li > a > i { 175 | display: inline-block; 176 | 177 | width: 24px; 178 | height: 24px; 179 | margin-top: 11px; 180 | margin-bottom: -3px; 181 | font-size: 20px; 182 | } 183 | 184 | .subnavbar .container > ul > li > a > span { 185 | display: block; 186 | 187 | } 188 | 189 | 190 | .subnavbar .container > ul > li:hover > a { 191 | 192 | border-bottom:3px solid #ff7f74; 193 | color: #383838; 194 | } 195 | 196 | 197 | .subnavbar .dropdown .dropdown-menu a { 198 | font-size: 12px; 199 | } 200 | 201 | 202 | .subnavbar .dropdown .dropdown-menu { 203 | text-align: left; 204 | 205 | -webkit-border-top-left-radius: 0; 206 | -webkit-border-top-right-radius: 0; 207 | -moz-border-radius-topleft: 0; 208 | -moz-border-radius-topright: 0; 209 | border-top-left-radius: 0; 210 | border-top-right-radius: 0; 211 | } 212 | 213 | 214 | 215 | .subnavbar .dropdown-menu::before { 216 | content: ''; 217 | display: inline-block; 218 | border-left: 7px solid transparent; 219 | border-right: 7px solid transparent; 220 | border-bottom: 7px solid #CCC; 221 | border-bottom-color: rgba(0, 0, 0, 0.2); 222 | position: absolute; 223 | top: -7px; 224 | left: 9px; 225 | } 226 | 227 | .subnavbar .dropdown-menu::after { 228 | content: ''; 229 | display: inline-block; 230 | border-left: 6px solid transparent; 231 | border-right: 6px solid transparent; 232 | border-bottom: 6px solid white; 233 | position: absolute; 234 | top: -6px; 235 | left: 10px; 236 | } 237 | 238 | 239 | .subnavbar .caret { 240 | margin-top: 4px; 241 | 242 | border-top-color: white; 243 | border-bottom-color: white; 244 | } 245 | 246 | .subnavbar .dropdown.open .caret { 247 | display: none; 248 | } 249 | 250 | 251 | 252 | 253 | 254 | /*------------------------------------------------------------------ 255 | [4. Main / .main] 256 | 257 | 258 | .main { 259 | padding-bottom: 2em; 260 | 261 | border-bottom: 1px solid #000; 262 | } 263 | */ 264 | 265 | 266 | /*------------------------------------------------------------------ 267 | [5. Extra / .extra] 268 | */ 269 | 270 | .extra { 271 | 272 | border-top: 1px solid #585858; 273 | border-bottom: 1px solid #000; 274 | 275 | } 276 | 277 | .extra-inner { 278 | padding: 20px 0; 279 | 280 | font-size: 11px; 281 | color: #BBB; 282 | 283 | background: #1A1A1A; 284 | } 285 | 286 | .extra a { 287 | color: #666; 288 | } 289 | 290 | .extra h4 { 291 | margin-bottom: 1em; 292 | 293 | font-weight: 400; 294 | } 295 | 296 | .extra ul { 297 | padding: 0; 298 | margin: 0; 299 | } 300 | 301 | .extra li { 302 | margin-bottom: .6em; 303 | 304 | list-style: none; 305 | } 306 | 307 | 308 | 309 | 310 | /*------------------------------------------------------------------ 311 | [6. Footer/ .footer] 312 | */ 313 | 314 | .footer { 315 | margin-top: 0; 316 | 317 | border-top: 1px solid #292929; 318 | } 319 | 320 | .footer-inner { 321 | padding: 15px 0; 322 | 323 | font-size: 12px; 324 | background: #111; 325 | color: #999; 326 | } 327 | 328 | .footer a { 329 | color: #999; 330 | } 331 | 332 | .footer a:hover { 333 | color: #FFF; 334 | text-decoration: none; 335 | } 336 | 337 | 338 | /*------------------------------------------------------------------ 339 | [6. Widget / .widget] 340 | */ 341 | 342 | .widget { 343 | 344 | position: relative; 345 | clear: both; 346 | 347 | width: auto; 348 | 349 | margin-bottom: 2em; 350 | 351 | overflow: hidden; 352 | } 353 | 354 | .widget-header { 355 | position: relative; 356 | cursor: move; 357 | height: 40px; 358 | line-height: 40px; 359 | 360 | background: #f9f6f1; 361 | background:-moz-linear-gradient(top, #f9f6f1 0%, #f2efea 100%); /* FF3.6+ */ 362 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f6f1), color-stop(100%,#f2efea)); /* Chrome,Safari4+ */ 363 | background:-webkit-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* Chrome10+,Safari5.1+ */ 364 | background:-o-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* Opera11.10+ */ 365 | background:-ms-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* IE10+ */ 366 | background:linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* W3C */ 367 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f6f1', endColorstr='#f2efea'); 368 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f6f1', endColorstr='#f2efea')"; 369 | 370 | 371 | border: 1px solid #d6d6d6; 372 | 373 | 374 | -webkit-background-clip: padding-box; 375 | } 376 | 377 | .widget-header h3 { 378 | position: relative; 379 | top: 2px; 380 | left: 10px; 381 | 382 | display: inline-block; 383 | margin-right: 3em; 384 | 385 | font-size: 14px; 386 | font-weight: 800; 387 | color: #525252; 388 | line-height: 18px; 389 | 390 | text-shadow: 1px 1px 2px rgba(255,255,255,.5); 391 | } 392 | 393 | .widget-header [class^="icon-"], .widget-header [class*=" icon-"] { 394 | 395 | display: inline-block; 396 | margin-left: 13px; 397 | margin-right: -2px; 398 | 399 | font-size: 16px; 400 | color: #555; 401 | vertical-align: middle; 402 | } 403 | 404 | .widget-content { 405 | padding: 20px 15px 15px; 406 | background: #FFF; 407 | 408 | 409 | border: 1px solid #D5D5D5; 410 | 411 | -moz-border-radius: 5px; 412 | -webkit-border-radius: 5px; 413 | border-radius: 5px; 414 | } 415 | 416 | .widget-header+.widget-content { 417 | border-top: none; 418 | 419 | -webkit-border-top-left-radius: 0; 420 | -webkit-border-top-right-radius: 0; 421 | -moz-border-radius-topleft: 0; 422 | -moz-border-radius-topright: 0; 423 | border-top-left-radius: 0; 424 | border-top-right-radius: 0; 425 | } 426 | 427 | .widget-nopad .widget-content { 428 | padding: 0; 429 | } 430 | 431 | /* Widget Content Clearfix */ 432 | .widget-content:before, 433 | .widget-content:after { 434 | content:""; 435 | display:table; 436 | } 437 | 438 | .widget-content:after { 439 | clear:both; 440 | } 441 | 442 | /* For IE 6/7 (trigger hasLayout) */ 443 | .widget-content { 444 | zoom:1; 445 | } 446 | 447 | /* Widget Table */ 448 | 449 | .widget-table .widget-content { 450 | padding: 0; 451 | } 452 | 453 | .widget-table .table { 454 | margin-bottom: 0; 455 | 456 | border: none; 457 | } 458 | 459 | .widget-table .table tr td:first-child { 460 | border-left: none; 461 | } 462 | 463 | .widget-table .table tr th:first-child { 464 | border-left: none; 465 | } 466 | 467 | 468 | /* Widget Plain */ 469 | 470 | .widget-plain { 471 | 472 | background: transparent; 473 | 474 | border: none; 475 | } 476 | 477 | .widget-plain .widget-content { 478 | padding: 0; 479 | 480 | background: transparent; 481 | 482 | border: none; 483 | } 484 | 485 | 486 | /* Widget Box */ 487 | 488 | .widget-box { 489 | 490 | } 491 | 492 | .widget-box .widget-content { 493 | background: #E3E3E3; 494 | background: #FFF; 495 | } 496 | 497 | 498 | 499 | /*------------------------------------------------------------------ 500 | [7. Error / .error-container] 501 | */ 502 | 503 | .error-container { 504 | margin-top: 4em; 505 | margin-bottom: 4em; 506 | text-align: center; 507 | } 508 | 509 | .error-container h1 { 510 | margin-bottom: .5em; 511 | 512 | font-size: 120px; 513 | line-height: 1em; 514 | } 515 | 516 | .error-container h2 { 517 | margin-bottom: .75em; 518 | font-size: 28px; 519 | } 520 | 521 | .error-container .error-details { 522 | margin-bottom: 1.5em; 523 | 524 | font-size: 16px; 525 | } 526 | 527 | .error-container .error-actions a { 528 | margin: 0 .5em; 529 | } 530 | 531 | 532 | 533 | /* Message layout */ 534 | 535 | 536 | ul.messages_layout { 537 | position: relative; 538 | margin: 0; 539 | padding: 0 540 | } 541 | ul.messages_layout li { 542 | float: left; 543 | list-style: none; 544 | position: relative 545 | } 546 | ul.messages_layout li.left { 547 | padding-left: 75px 548 | } 549 | ul.messages_layout li.right { 550 | padding-right: 75px 551 | } 552 | ul.messages_layout li.right .avatar { 553 | right: 0; 554 | left: auto 555 | } 556 | ul.messages_layout li.right .message_wrap .arrow { 557 | right: -12px; 558 | left: auto; 559 | background-position: 0 -213px; 560 | height: 15px; 561 | width: 12px 562 | } 563 | ul.messages_layout li.by_myself .message_wrap { 564 | border: 1px solid #b3cdf8 565 | } 566 | ul.messages_layout li.by_myself .message_wrap .info a.name { 567 | color: #4a8cf7 568 | } 569 | ul.messages_layout li a.avatar { 570 | position: absolute; 571 | left: 0; 572 | top: 0 573 | } 574 | ul.messages_layout li a.avatar img { 575 | -webkit-border-radius: 5px; 576 | -moz-border-radius: 5px; 577 | border-radius: 5px 578 | } 579 | ul.messages_layout li .message_wrap { 580 | -webkit-border-radius: 3px; 581 | -moz-border-radius: 3px; 582 | border-radius: 3px; 583 | position: relative; 584 | border: 1px solid #e9e9e9; 585 | padding: 10px; 586 | border: 1px solid #cbcbcb; 587 | margin-bottom: 20px; 588 | float: left; 589 | background: #fefefe; 590 | -webkit-box-shadow: rgba(0,0,0,0.1) 0 1px 0px; 591 | -moz-box-shadow: rgba(0,0,0,0.1) 0 1px 0px; 592 | box-shadow: rgba(0,0,0,0.1) 0 1px 0px 593 | } 594 | ul.messages_layout li .message_wrap .arrow { 595 | background-position: 0 -228px; 596 | height: 15px; 597 | width: 12px; 598 | height: 15px; 599 | width: 12px; 600 | position: absolute; 601 | left: -12px; 602 | top: 13px 603 | } 604 | ul.messages_layout li .message_wrap .info { 605 | float: left; 606 | width: 100%; 607 | border-bottom: 1px solid #fff; 608 | line-height: 23px 609 | } 610 | ul.messages_layout li .message_wrap .info .name { 611 | float: left; 612 | font-weight: bold; 613 | color: #483734 614 | } 615 | ul.messages_layout li .message_wrap .info .time { 616 | float: left; 617 | font-size: 11px; 618 | margin-left: 6px 619 | } 620 | ul.messages_layout li .message_wrap .text { 621 | float: left; 622 | width: 100%; 623 | border-top: 1px solid #cfcfcf; 624 | padding-top: 5px 625 | } 626 | 627 | ul.messages_layout .dropdown-menu li{ width:100%; font-size:11px;} 628 | 629 | 630 | /* Full Calendar */ 631 | 632 | .fc { 633 | direction: ltr; 634 | text-align: left; 635 | position: relative 636 | } 637 | .fc table { 638 | border-collapse: collapse; 639 | border-spacing: 0 640 | } 641 | html .fc, .fc table { 642 | font-size: 1em 643 | } 644 | .fc td, .fc th { 645 | padding: 0; 646 | vertical-align: top 647 | } 648 | .fc-header td { 649 | white-space: nowrap; 650 | background: none 651 | } 652 | .fc-header-left { 653 | width: 100%; 654 | text-align: left; 655 | position: absolute; 656 | left: 0; 657 | top: 6px 658 | } 659 | .fc-header-left .fc-button { 660 | margin: 0; 661 | position: relative 662 | } 663 | .fc-header-left .fc-button-prev, .fc-header-left .fc-button-next { 664 | float: left; 665 | border: none; 666 | padding: 14px 10px; 667 | opacity: 0.5 668 | } 669 | .fc-header-left .fc-button-prev .fc-button-inner, .fc-header-left .fc-button-next .fc-button-inner { 670 | border: none 671 | } 672 | .fc-header-left .fc-button-prev .fc-button-inner .fc-button-content, .fc-header-left .fc-button-next .fc-button-inner .fc-button-content { 673 | display: none 674 | } 675 | .fc-header-left .fc-button-prev.fc-state-hover, .fc-header-left .fc-button-next.fc-state-hover { 676 | opacity: 1 677 | } 678 | .fc-header-left .fc-button-prev.fc-state-down, .fc-header-left .fc-button-next.fc-state-down { 679 | background: none !important; 680 | margin-top: -1px 681 | } 682 | .fc-header-left .fc-button-prev .fc-button-inner { 683 | background-position: 0 -351px; 684 | height: 16px; 685 | width: 11px 686 | } 687 | .fc-header-left .fc-button-next { 688 | float: right 689 | } 690 | .fc-header-left .fc-button-next .fc-button-inner { 691 | background-position: 0 -367px; 692 | height: 16px; 693 | width: 11px 694 | } 695 | .fc-header-center { 696 | text-align: center 697 | } 698 | .fc-header-right { 699 | text-align: right; 700 | position: absolute; 701 | top: -34px; 702 | right: 10px 703 | } 704 | .fc-header-title { 705 | display: inline-block; 706 | vertical-align: top 707 | } 708 | .fc-header-title h2 { 709 | margin-top: 0; 710 | white-space: nowrap; 711 | font-size: 1.1rem; 712 | color: #6C737F; 713 | line-height: 55px; 714 | } 715 | .fc .fc-header-space { 716 | padding-left: 10px 717 | } 718 | .fc-header .fc-button { 719 | margin-bottom: 1em; 720 | vertical-align: top 721 | } 722 | .fc-header .fc-button { 723 | margin-right: -1px 724 | } 725 | .fc-header .fc-corner-right { 726 | margin-right: 1px 727 | } 728 | .fc-header .ui-corner-right { 729 | margin-right: 0 730 | } 731 | .fc-header .fc-state-hover, .fc-header .ui-state-hover { 732 | z-index: 2 733 | } 734 | .fc-header .fc-state-down { 735 | z-index: 3 736 | } 737 | .fc-header .fc-state-active, .fc-header .ui-state-active { 738 | z-index: 4 739 | } 740 | .fc-content { 741 | clear: both; 742 | background: #f9f9f9 743 | } 744 | .fc-view { 745 | width: 100%; 746 | overflow: hidden 747 | } 748 | .fc-view thead { 749 | background:#e9ecf1; 750 | line-height: 35px 751 | } 752 | .fc-widget-header, .fc-widget-content { 753 | border: 1px solid #ccc 754 | } 755 | .fc-state-highlight { 756 | background: #F4F3E6 757 | } 758 | .fc-cell-overlay { 759 | background: #9cf; 760 | opacity: .2; 761 | filter: alpha(opacity=20) 762 | } 763 | .fc-button { 764 | position: relative; 765 | display: inline-block; 766 | cursor: pointer 767 | } 768 | .fc-button-today{margin-top: 8px !important;} 769 | .fc-state-default { 770 | border-style: solid; 771 | border-width: 1px 0 772 | } 773 | .fc-button-inner { 774 | position: relative; 775 | float: left; 776 | overflow: hidden 777 | } 778 | .fc-state-default .fc-button-inner { 779 | border-style: solid; 780 | border-width: 0 1px 781 | } 782 | .fc-button-content { 783 | position: relative; 784 | float: left; 785 | height: 1.9em; 786 | line-height: 1.9em; 787 | padding: 0 .6em; 788 | white-space: nowrap 789 | } 790 | .fc-button-content .fc-icon-wrap { 791 | position: relative; 792 | float: left; 793 | top: 50% 794 | } 795 | .fc-button-content .ui-icon { 796 | position: relative; 797 | float: left; 798 | margin-top: -50%; 799 | *margin-top:0; 800 | *top:-50% 801 | } 802 | .fc-state-default .fc-button-effect { 803 | position: absolute; 804 | top: 50%; 805 | left: 0 806 | } 807 | .fc-state-default .fc-button-effect span { 808 | position: absolute; 809 | top: -100px; 810 | left: 0; 811 | width: 500px; 812 | height: 100px; 813 | border-width: 100px 0 0 1px; 814 | border-style: solid; 815 | border-color: #fff; 816 | background: #444; 817 | opacity: .09; 818 | filter: alpha(opacity=9) 819 | } 820 | .fc-state-default, .fc-state-default .fc-button-inner { 821 | border-style: solid; 822 | border-color: #ccc #bbb #aaa; 823 | color: #000 824 | } 825 | .fc-state-hover, .fc-state-hover .fc-button-inner { 826 | border-color: #999 827 | } 828 | .fc-state-down { 829 | border-color: #555; 830 | background: #777 831 | } 832 | .fc-state-active, .fc-state-active .fc-button-inner { 833 | border-color: #555; 834 | background: #777; 835 | color: #fff 836 | } 837 | .fc-state-disabled, .fc-state-disabled .fc-button-inner { 838 | color: #999; 839 | border-color: #ddd 840 | } 841 | .fc-state-disabled { 842 | cursor: default 843 | } 844 | .fc-state-disabled .fc-button-effect { 845 | display: none 846 | } 847 | .fc-event { 848 | border-style: solid; 849 | border-width: 0; 850 | font-size: .85em; 851 | cursor: default 852 | } 853 | a.fc-event, .fc-event-draggable { 854 | cursor: pointer 855 | } 856 | a.fc-event { 857 | text-decoration: none 858 | } 859 | .fc-rtl .fc-event { 860 | text-align: right 861 | } 862 | .fc-event-skin { 863 | border-color: #3f85f5; 864 | background-color: #5e96ea; 865 | color: #fff 866 | } 867 | .fc-event-inner { 868 | position: relative; 869 | width: 100%; 870 | height: 100%; 871 | border-style: solid; 872 | border-width: 0; 873 | overflow: hidden 874 | } 875 | .fc-event-time, .fc-event-title { 876 | padding: 0 1px 877 | } 878 | .fc .ui-resizable-handle { 879 | display: block; 880 | position: absolute; 881 | z-index: 99999; 882 | overflow: hidden; 883 | font-size: 300%; 884 | line-height: 50% 885 | } 886 | .fc-event-hori { 887 | border-width: 1px 0; 888 | margin-bottom: 1px 889 | } 890 | .fc-event-hori .ui-resizable-e { 891 | top: 0 !important; 892 | right: -3px !important; 893 | width: 7px !important; 894 | height: 100% !important; 895 | cursor: e-resize 896 | } 897 | .fc-event-hori .ui-resizable-w { 898 | top: 0 !important; 899 | left: -3px !important; 900 | width: 7px !important; 901 | height: 100% !important; 902 | cursor: w-resize 903 | } 904 | .fc-event-hori .ui-resizable-handle { 905 | _padding-bottom: 14px 906 | } 907 | .fc-corner-left { 908 | margin-left: 1px 909 | } 910 | .fc-corner-left .fc-button-inner, .fc-corner-left .fc-event-inner { 911 | margin-left: -1px 912 | } 913 | .fc-corner-right { 914 | margin-right: 1px 915 | } 916 | .fc-corner-right .fc-button-inner, .fc-corner-right .fc-event-inner { 917 | margin-right: -1px 918 | } 919 | .fc-corner-top { 920 | margin-top: 1px 921 | } 922 | .fc-corner-top .fc-event-inner { 923 | margin-top: -1px 924 | } 925 | .fc-corner-bottom { 926 | margin-bottom: 1px 927 | } 928 | .fc-corner-bottom .fc-event-inner { 929 | margin-bottom: -1px 930 | } 931 | .fc-corner-left .fc-event-inner { 932 | border-left-width: 1px 933 | } 934 | .fc-corner-right .fc-event-inner { 935 | border-right-width: 1px 936 | } 937 | .fc-corner-top .fc-event-inner { 938 | border-top-width: 1px 939 | } 940 | .fc-corner-bottom .fc-event-inner { 941 | border-bottom-width: 1px 942 | } 943 | table.fc-border-separate { 944 | border-collapse: separate 945 | } 946 | .fc-border-separate th, .fc-border-separate td { 947 | border-width: 1px 0 0 1px 948 | } 949 | .fc-border-separate th.fc-last, .fc-border-separate td.fc-last { 950 | border-right-width: 1px 951 | } 952 | .fc-border-separate tr.fc-last th, .fc-border-separate tr.fc-last td { 953 | border-bottom-width: 0px 954 | } 955 | .fc-first { 956 | border-left-width: 0 !important 957 | } 958 | .fc-last { 959 | border-right-width: 0 !important 960 | } 961 | .fc-grid th { 962 | text-align: center 963 | } 964 | .fc-grid .fc-day-number { 965 | float: right; 966 | padding: 0 2px 967 | } 968 | .fc-grid .fc-other-month .fc-day-number { 969 | opacity: 0.3; 970 | filter: alpha(opacity=30) 971 | } 972 | .fc-grid .fc-day-content { 973 | clear: both; 974 | padding: 2px 2px 1px 975 | } 976 | .fc-grid .fc-event-time { 977 | font-weight: bold 978 | } 979 | .fc-rtl .fc-grid .fc-day-number { 980 | float: left 981 | } 982 | .fc-rtl .fc-grid .fc-event-time { 983 | float: right 984 | } 985 | .fc-agenda table { 986 | border-collapse: separate 987 | } 988 | .fc-agenda-days th { 989 | text-align: center 990 | } 991 | .fc-agenda .fc-agenda-axis { 992 | width: 60px !important; 993 | padding: 0 4px; 994 | vertical-align: middle; 995 | text-align: right; 996 | white-space: nowrap; 997 | font-weight: normal 998 | } 999 | .fc-agenda .fc-day-content { 1000 | padding: 2px 2px 1px 1001 | } 1002 | .fc-agenda-days .fc-agenda-axis { 1003 | border-right-width: 1px 1004 | } 1005 | .fc-agenda-days .fc-col0 { 1006 | border-left-width: 0 1007 | } 1008 | .fc-agenda-allday th { 1009 | border-width: 0 1px 1010 | } 1011 | .fc-agenda-allday .fc-day-content { 1012 | min-height: 34px; 1013 | _height: 34px 1014 | } 1015 | .fc-agenda-divider-inner { 1016 | height: 2px; 1017 | overflow: hidden 1018 | } 1019 | .fc-widget-header .fc-agenda-divider-inner { 1020 | background: #eee 1021 | } 1022 | .fc-agenda-slots th { 1023 | border-width: 1px 1px 0 1024 | } 1025 | .fc-agenda-slots td { 1026 | border-width: 1px 0 0; 1027 | background: none 1028 | } 1029 | .fc-agenda-slots td div { 1030 | height: 20px 1031 | } 1032 | .fc-agenda-slots tr.fc-slot0 th, .fc-agenda-slots tr.fc-slot0 td { 1033 | border-top-width: 0 1034 | } 1035 | .fc-agenda-slots tr.fc-minor th, .fc-agenda-slots tr.fc-minor td { 1036 | border-top-style: dotted 1037 | } 1038 | .fc-agenda-slots tr.fc-minor th.ui-widget-header { 1039 | *border-top-style:solid 1040 | } 1041 | .fc-event-vert { 1042 | border-width: 0 1px 1043 | } 1044 | .fc-event-vert .fc-event-head, .fc-event-vert .fc-event-content { 1045 | position: relative; 1046 | z-index: 2; 1047 | width: 100%; 1048 | overflow: hidden 1049 | } 1050 | .fc-event-vert .fc-event-time { 1051 | white-space: nowrap; 1052 | font-size: 10px 1053 | } 1054 | .fc-event-vert .fc-event-bg { 1055 | position: absolute; 1056 | z-index: 1; 1057 | top: 0; 1058 | left: 0; 1059 | width: 100%; 1060 | height: 100%; 1061 | background: #fff; 1062 | opacity: .3; 1063 | filter: alpha(opacity=30) 1064 | } 1065 | .fc .ui-draggable-dragging .fc-event-bg, .fc-select-helper .fc-event-bg { 1066 | display: none\9 1067 | } 1068 | .fc-event-vert .ui-resizable-s { 1069 | bottom: 0 !important; 1070 | width: 100% !important; 1071 | height: 8px !important; 1072 | overflow: hidden !important; 1073 | line-height: 8px !important; 1074 | font-size: 11px !important; 1075 | font-family: monospace; 1076 | text-align: center; 1077 | cursor: s-resize 1078 | } 1079 | .fc-agenda .ui-resizable-resizing { 1080 | _overflow: hidden 1081 | } 1082 | .fc-header-left .fc-button-prev .fc-button-inner {background: url('../img/icons-sa7c41345d9.png') no-repeat; background-position: 0 -351px; 1083 | height: 16px; 1084 | width: 11px;} 1085 | 1086 | .fc-header-left .fc-button-next .fc-button-inner {background: url('../img/icons-sa7c41345d9.png') no-repeat; background-position: 0 -367px; 1087 | height: 16px; 1088 | width: 11px;} 1089 | 1090 | /*------------------------------------------------------------------ 1091 | [8. Miscellaneous] 1092 | */ 1093 | 1094 | .chart-holder { 1095 | width: 100%; 1096 | height: 250px; 1097 | } 1098 | 1099 | .dropdown-menu li>a:hover, .dropdown-menu .active>a, .dropdown-menu .active>a:hover { background:#00ba8b;} 1100 | 1101 | .accordion-heading { background:#e5e5e5; } 1102 | .accordion-heading a { color:#545454; text-decoration:none; font-weight:bold; } 1103 | 1104 | .btn-facebook-alt i { 1105 | color: #23386a; 1106 | } 1107 | .btn-twitter-alt i { 1108 | color: #0098d0; 1109 | } 1110 | .btn-google-alt i { 1111 | color: #b6362d; 1112 | } 1113 | .btn-linkedin-alt i { 1114 | color: #0073b2; 1115 | } 1116 | .btn-pinterest-alt i { 1117 | color: #ab171e; 1118 | } 1119 | .btn-github-alt i { 1120 | color: #333; 1121 | } 1122 | 1123 | .all-icons li { list-style:none;} 1124 | 1125 | .ML0 { margin-left:0} 1126 | .MR0 { margin-right:0;} 1127 | 1128 | .paginate_active { text-decoration: underline; } 1129 | 1130 | #refresh-ispeed .icon-refresh, #refresh-ping .icon-refresh, #refresh-bandwidth .icon-refresh { 1131 | margin: 0; 1132 | } 1133 | 1134 | /*------------------------------------------------------------------ 1135 | [1. Max Width: 480px] 1136 | */ 1137 | 1138 | @media (max-width: 480px) { 1139 | 1140 | .error-container h1 { 1141 | font-size: 72px; 1142 | } 1143 | 1144 | } 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | /*------------------------------------------------------------------ 1151 | [1. Max Width: 767px] 1152 | */ 1153 | 1154 | @media (max-width: 767px) { 1155 | 1156 | #main { 1157 | padding: 0 10px; 1158 | margin-right: -20px; 1159 | margin-left: -20px; 1160 | } 1161 | 1162 | 1163 | .subnavbar { 1164 | margin-left: -20px; 1165 | margin-right: -20px; 1166 | } 1167 | 1168 | 1169 | .subnavbar-inner { 1170 | height: auto; 1171 | } 1172 | 1173 | .subnavbar .container > ul { 1174 | width: 100%; 1175 | height: auto; 1176 | 1177 | border: none; 1178 | } 1179 | 1180 | .subnavbar .container > ul > li { 1181 | width: 33%; 1182 | height: 70px; 1183 | margin-bottom: 0; 1184 | 1185 | border: none; 1186 | } 1187 | 1188 | 1189 | 1190 | .subnavbar .container > ul > li.active > a { 1191 | font-size: 11px; 1192 | background: transparent; 1193 | } 1194 | 1195 | .subnavbar .container > ul > li > a > i { 1196 | display: inline-block; 1197 | margin-bottom: 0; 1198 | 1199 | font-size: 20px; 1200 | } 1201 | 1202 | 1203 | .subnavbar-open-right .dropdown-menu { 1204 | left: auto; 1205 | right: 0; 1206 | } 1207 | 1208 | .subnavbar-open-right .dropdown-menu:before { 1209 | left: auto; 1210 | right: 12px; 1211 | } 1212 | .subnavbar-open-right .dropdown-menu:after { 1213 | left: auto; 1214 | right: 13px; 1215 | } 1216 | 1217 | .extra { 1218 | margin-right: -20px; 1219 | margin-left: -20px; 1220 | } 1221 | 1222 | .extra .container { 1223 | padding: 0 20px; 1224 | } 1225 | 1226 | .footer { 1227 | margin-right: -20px; 1228 | margin-left: -20px; 1229 | } 1230 | 1231 | .footer .container { 1232 | padding: 0 20px; 1233 | } 1234 | 1235 | .footer .footer-terms { 1236 | text-align: left; 1237 | } 1238 | 1239 | .footer .footer-terms a { 1240 | margin-left: 0; 1241 | margin-right: 1em; 1242 | } 1243 | 1244 | } 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | /*------------------------------------------------------------------ 1251 | [2. Max Width: 979px] 1252 | */ 1253 | 1254 | @media (max-width: 979px) { 1255 | 1256 | .navbar-fixed-top { 1257 | position: static; 1258 | 1259 | margin-bottom: 0; 1260 | } 1261 | 1262 | .subnavbar { 1263 | } 1264 | 1265 | .subnavbar .container { 1266 | width: auto; 1267 | } 1268 | 1269 | .widget-header { 1270 | height: 100%; 1271 | } 1272 | 1273 | .widget-header h3 { 1274 | margin-right: 1em; 1275 | } 1276 | 1277 | .js-refresh-info { 1278 | margin-left: 25px; 1279 | } 1280 | } 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | /*------------------------------------------------------------------ 1288 | [3. Max Width: 1199px] 1289 | */ 1290 | 1291 | @media (max-width: 1199px) { 1292 | .navbar .search-query { 1293 | width: 200px; 1294 | } 1295 | 1296 | .widget-header { 1297 | height: 100%; 1298 | } 1299 | 1300 | .js-refresh-info { 1301 | margin-left: 25px; 1302 | } 1303 | } 1304 | 1305 | /* ---------------------------------------- 1306 | Subnavbar CSS fix/work-around 1307 | */ 1308 | 1309 | .container.nav-collapse.in { 1310 | overflow:visible; 1311 | } 1312 | .btn-navbar { 1313 | margin-top:-38px; 1314 | } 1315 | 1316 | /*------------------------------------------------------------------ 1317 | general-info widget specific style 1318 | */ 1319 | 1320 | #general-info-widget .general-title { 1321 | font-weight: bold; 1322 | } 1323 | 1324 | #general-info-widget .general-title:after { 1325 | content:":"; 1326 | } 1327 | 1328 | #general-info-widget .widget-content { 1329 | padding:5px; 1330 | } 1331 | 1332 | #general-info-widget .widget-content div { 1333 | margin-bottom:5px; 1334 | } 1335 | 1336 | footer { 1337 | border-top: 1px solid #D8D7CF; 1338 | clear: both; 1339 | color: #9A9994; 1340 | font-size: 12px; 1341 | line-height: 15.4px; 1342 | margin-top: 15px; 1343 | overflow: hidden; 1344 | padding: 20px 0 40px; 1345 | } 1346 | footer .site-source { 1347 | background: url("../img/code.png") no-repeat scroll 0 2px transparent; 1348 | float: left; 1349 | padding-left: 46px; 1350 | } 1351 | footer .sfc-member {float: right;text-align: right;} 1352 | footer a {color: #403F3C;} 1353 | -------------------------------------------------------------------------------- /public/img/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/img/code.png -------------------------------------------------------------------------------- /public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-falcon-archive/agent/3eb5031bd1d0d70d5b0c13085274ba00a2374594/public/img/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | linux-dash : Server Monitoring Web Dashboard 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 39 | 74 |
75 |
76 |
77 |
78 |
79 |
80 | A simple web dashboard to monitor your server. 81 |
82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 | 90 |

91 | General Info 92 |

93 |
94 |
95 |
96 |
97 | Kernel Version 98 | 99 |
100 |
101 | Uptime 102 | 103 |
104 |
105 | Server Time 106 | 107 |
108 |
109 | Hostname 110 | 111 |
112 |
113 | Agent Version 114 | 115 |
116 |
117 |
118 |
119 | 120 |
121 |
122 |
123 | 124 |

125 | Load average 126 |

127 |
128 |
129 |
130 |
131 | Number of cores: 0 132 |
133 |
134 |
135 | 1 min  %
136 | 137 |
138 |
139 | 5 min  %
140 | 141 |
142 |
143 | 15 min  %
144 | 145 |
146 |
147 |
148 |
149 |
150 | 151 |
152 |
153 |
154 | 155 |

156 | RAM 157 |

158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | Total  MB 166 |
167 |
168 | Used  MB
169 | % 170 |
171 |
172 | Free  MB
173 | % 174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | 183 |
184 |
185 |
186 |
187 | 188 |

189 | Disk Usage 190 |

191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 | 200 | 201 |
202 |
203 |
204 |
205 | 206 |

207 | Disk IO Stats 208 |

209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 | 218 |
219 |
220 |
221 |
222 | 223 |

224 | CPU 225 |

226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | 235 |
236 | 237 | 238 |
239 |
240 |
241 | Falcon-Agent is open source on GitHub.
242 | Patches, suggestions, and comments are welcome. 243 |
244 |
245 | Powered by UlricQin 246 |
247 |
248 | 249 | 250 |
251 |
252 |
253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /public/js/base.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | // enable popovers 4 | $(".pop").popover(); 5 | 6 | // activate tooltips on hover 7 | $("[data-toggle='tooltip']").tooltip({trigger: 'hover', placement:'right'}); 8 | 9 | dashboard.getAll(); 10 | }).on("click", ".js-smoothscroll", function(event) { 11 | event.preventDefault(); 12 | var target = $(this.hash).parent(); 13 | pulseElement(target, 8, 400); 14 | 15 | $("html,body").animate({ 16 | scrollTop: target.offset().top - 130 17 | }, 1000); 18 | }).on("click", ".js-refresh-info", function(event) { 19 | event.preventDefault(); 20 | var target = event.target; 21 | var item = target.id.split("-").splice(-1)[0]; 22 | 23 | // if the refresh icon is click (where in a ) target will not have an id, so grab its parent instead 24 | if(target.id == "") { 25 | var parent = $(target).parent()[0]; 26 | item = parent.id.split("-").splice(-1)[0]; 27 | } 28 | 29 | dashboard.fnMap[item](); 30 | }); 31 | 32 | // Handle for cancelling active effect. 33 | var pulsing = { 34 | element: null, 35 | timeoutIDs: [], 36 | resetfn: function() { 37 | pulsing.element = null; 38 | pulsing.timeoutIDs = []; 39 | } 40 | }; 41 | 42 | /** 43 | * Applies a pulse effect to the specified element. If triggered while already 44 | * active the ongoing effect is cancelled immediately. 45 | * 46 | * @param {HTMLElement} element The element to apply the effect to. 47 | * @param {Number} times How many pulses. 48 | * @param {Number} interval Milliseconds between pulses. 49 | */ 50 | function pulseElement(element, times, interval) { 51 | if (pulsing.element) { 52 | pulsing.element.removeClass("pulse"). 53 | parent().removeClass("pulse-border"); 54 | pulsing.timeoutIDs.forEach(function(ID) { 55 | clearTimeout(ID); 56 | }); 57 | pulsing.timeoutIDs = []; 58 | } 59 | pulsing.element = element; 60 | var parent = element.parent(); 61 | var f = function() { 62 | element.toggleClass("pulse"); 63 | parent.toggleClass("pulse-border"); 64 | }; 65 | 66 | pulsing.timeoutIDs.push(setTimeout(pulsing.resetfn, 67 | (times + 1) * interval)); 68 | for (; times > 0; --times) { 69 | pulsing.timeoutIDs.push(setTimeout(f, times * interval)); 70 | } 71 | } 72 | 73 | function isInArray(array, search) 74 | { 75 | return (array.indexOf(search) >= 0) ? true : false; 76 | } 77 | -------------------------------------------------------------------------------- /public/js/dashboard.js: -------------------------------------------------------------------------------- 1 | // Gets data from provided url and updates DOM element. 2 | function generate_os_data(url, element) { 3 | $.get(url, function (d) { 4 | if (d.msg == "success") { 5 | $(element).text(d.data); 6 | } else { 7 | $(element).text(d.msg); 8 | } 9 | }, "json"); 10 | } 11 | 12 | // If dataTable with provided ID exists, destroy it. 13 | function destroy_dataTable(table_id) { 14 | var table = $("#" + table_id); 15 | var ex = document.getElementById(table_id); 16 | if ($.fn.DataTable.fnIsDataTable(ex)) { 17 | table.hide().dataTable().fnClearTable(); 18 | table.dataTable().fnDestroy(); 19 | } 20 | } 21 | 22 | //DataTables 23 | //Sort file size data. 24 | jQuery.extend(jQuery.fn.dataTableExt.oSort, { 25 | "file-size-units": { 26 | K: 1024, 27 | M: Math.pow(1024, 2), 28 | G: Math.pow(1024, 3), 29 | T: Math.pow(1024, 4), 30 | P: Math.pow(1024, 5), 31 | E: Math.pow(1024, 6) 32 | }, 33 | 34 | "file-size-pre": function (a) { 35 | var x = a.substring(0, a.length - 1); 36 | var x_unit = a.substring(a.length - 1, a.length); 37 | if (jQuery.fn.dataTableExt.oSort['file-size-units'][x_unit]) { 38 | return parseInt(x * jQuery.fn.dataTableExt.oSort['file-size-units'][x_unit], 10); 39 | } 40 | else { 41 | return parseInt(x + x_unit, 10); 42 | } 43 | }, 44 | 45 | "file-size-asc": function (a, b) { 46 | return ((a < b) ? -1 : ((a > b) ? 1 : 0)); 47 | }, 48 | 49 | "file-size-desc": function (a, b) { 50 | return ((a < b) ? 1 : ((a > b) ? -1 : 0)); 51 | } 52 | }); 53 | 54 | //DataTables 55 | //Sort numeric data which has a percent sign with it. 56 | jQuery.extend(jQuery.fn.dataTableExt.oSort, { 57 | "percent-pre": function (a) { 58 | var x = (a === "-") ? 0 : a.replace(/%/, ""); 59 | return parseFloat(x); 60 | }, 61 | 62 | "percent-asc": function (a, b) { 63 | return ((a < b) ? -1 : ((a > b) ? 1 : 0)); 64 | }, 65 | 66 | "percent-desc": function (a, b) { 67 | return ((a < b) ? 1 : ((a > b) ? -1 : 0)); 68 | } 69 | }); 70 | 71 | //DataTables 72 | //Sort IP addresses 73 | jQuery.extend(jQuery.fn.dataTableExt.oSort, { 74 | "ip-address-pre": function (a) { 75 | // split the address into octets 76 | // 77 | var x = a.split('.'); 78 | 79 | // pad each of the octets to three digits in length 80 | // 81 | function zeroPad(num, places) { 82 | var zero = places - num.toString().length + 1; 83 | return Array(+(zero > 0 && zero)).join("0") + num; 84 | } 85 | 86 | // build the resulting IP 87 | var r = ''; 88 | for (var i = 0; i < x.length; i++) 89 | r = r + zeroPad(x[i], 3); 90 | 91 | // return the formatted IP address 92 | // 93 | return r; 94 | }, 95 | 96 | "ip-address-asc": function (a, b) { 97 | return ((a < b) ? -1 : ((a > b) ? 1 : 0)); 98 | }, 99 | 100 | "ip-address-desc": function (a, b) { 101 | return ((a < b) ? 1 : ((a > b) ? -1 : 0)); 102 | } 103 | }); 104 | 105 | /******************************* 106 | Data Call Functions 107 | *******************************/ 108 | 109 | var dashboard = {}; 110 | 111 | dashboard.getRam = function () { 112 | $.get("page/memory", function (data) { 113 | if (data.msg == "success") { 114 | var ram_total = data.data[0]; 115 | var ram_used = Math.round((data.data[1] / ram_total) * 100); 116 | var ram_free = Math.round((data.data[2] / ram_total) * 100); 117 | 118 | $("#ram-total").text(ram_total); 119 | $("#ram-used").text(data.data[1]); 120 | $("#ram-free").text(data.data[2]); 121 | 122 | $("#ram-free-per").text(ram_free); 123 | $("#ram-used-per").text(ram_used); 124 | } 125 | }, "json"); 126 | } 127 | 128 | dashboard.getDf = function () { 129 | $.get("page/df", function (data) { 130 | var table = $("#df_dashboard"); 131 | var ex = document.getElementById("df_dashboard"); 132 | if ($.fn.DataTable.fnIsDataTable(ex)) { 133 | table.hide().dataTable().fnClearTable(); 134 | table.dataTable().fnDestroy(); 135 | } 136 | 137 | table.dataTable({ 138 | aaData: data.data, 139 | aoColumns: [ 140 | { sTitle: "Filesystem" }, 141 | { sTitle: "BTotal", sType: "file-size" }, 142 | { sTitle: "BUsed", sType: "file-size" }, 143 | { sTitle: "BFree", sType: "file-size" }, 144 | { sTitle: "BUse%", sType: "percent" }, 145 | { sTitle: "Mounted" }, 146 | { sTitle: "ITotal", sType: "file-size" }, 147 | { sTitle: "IUsed", sType: "file-size" }, 148 | { sTitle: "IFree", sType: "file-size" }, 149 | { sTitle: "IUse%", sType: "percent" }, 150 | { sTitle: "Vfstype" } 151 | ], 152 | bPaginate: false, 153 | bFilter: false, 154 | bAutoWidth: true, 155 | bInfo: false 156 | }).fadeIn(); 157 | }, "json"); 158 | } 159 | 160 | dashboard.getCpu = function () { 161 | $.get("page/cpu/usage", function (data) { 162 | if (data.msg != "success") { 163 | return 164 | } 165 | 166 | var table = $("#cpu_dashboard"); 167 | var ex = document.getElementById("cpu_dashboard"); 168 | if ($.fn.DataTable.fnIsDataTable(ex)) { 169 | table.hide().dataTable().fnClearTable(); 170 | table.dataTable().fnDestroy(); 171 | } 172 | 173 | table.dataTable({ 174 | aaData: data.data, 175 | aoColumns: [ 176 | { sTitle: "idle"}, 177 | { sTitle: "busy"}, 178 | { sTitle: "user"}, 179 | { sTitle: "nice"}, 180 | { sTitle: "system"}, 181 | { sTitle: "iowait"}, 182 | { sTitle: "irq"}, 183 | { sTitle: "softirq"}, 184 | { sTitle: "steal"}, 185 | { sTitle: "guest"} 186 | ], 187 | bPaginate: false, 188 | bFilter: false, 189 | bAutoWidth: true, 190 | bInfo: false 191 | }).fadeIn(); 192 | }, "json"); 193 | } 194 | 195 | dashboard.getDiskstats = function () { 196 | $.get("page/diskio", function (data) { 197 | var table = $("#diskstats_dashboard"); 198 | var ex = document.getElementById("diskstats_dashboard"); 199 | if ($.fn.DataTable.fnIsDataTable(ex)) { 200 | table.hide().dataTable().fnClearTable(); 201 | table.dataTable().fnDestroy(); 202 | } 203 | 204 | table.dataTable({ 205 | aaData: data.data, 206 | aoColumns: [ 207 | { sTitle: "Device"}, 208 | { sTitle: "rrqm/s"}, 209 | { sTitle: "wrqm/s"}, 210 | { sTitle: "r/s"}, 211 | { sTitle: "w/s"}, 212 | { sTitle: "rkB/s"}, 213 | { sTitle: "wkB/s"}, 214 | { sTitle: "avgrq-sz"}, 215 | { sTitle: "avgqu-sz"}, 216 | { sTitle: "await"}, 217 | { sTitle: "svctm"}, 218 | { sTitle: "%util"}, 219 | ], 220 | bPaginate: false, 221 | bFilter: false, 222 | bAutoWidth: true, 223 | bInfo: false 224 | }).fadeIn(); 225 | }, "json"); 226 | } 227 | 228 | dashboard.getOs = function () { 229 | generate_os_data("proc/kernel/version", "#os-info"); 230 | generate_os_data("proc/kernel/hostname", "#os-hostname"); 231 | generate_os_data("system/date", "#os-time"); 232 | generate_os_data("page/system/uptime", "#os-uptime"); 233 | 234 | $.get("version", function(d){ 235 | $("#agent-version").text(d); 236 | }); 237 | } 238 | 239 | dashboard.getLoadAverage = function () { 240 | $.get("page/system/loadavg", function (d) { 241 | if (d.msg != "success") { 242 | return 243 | } 244 | $("#cpu-1min").text(d.data[0][0]); 245 | $("#cpu-5min").text(d.data[1][0]); 246 | $("#cpu-15min").text(d.data[2][0]); 247 | $("#cpu-1min-per").text(d.data[0][1]); 248 | $("#cpu-5min-per").text(d.data[1][1]); 249 | $("#cpu-15min-per").text(d.data[2][1]); 250 | }, "json"); 251 | generate_os_data("proc/cpu/num", "#core-number"); 252 | } 253 | 254 | /** 255 | * Refreshes all widgets. Does not call itself recursively. 256 | */ 257 | dashboard.getAll = function () { 258 | for (var item in dashboard.fnMap) { 259 | if (dashboard.fnMap.hasOwnProperty(item) && item !== "all") { 260 | dashboard.fnMap[item](); 261 | } 262 | } 263 | } 264 | 265 | dashboard.fnMap = { 266 | all: dashboard.getAll, 267 | ram: dashboard.getRam, 268 | df: dashboard.getDf, 269 | os: dashboard.getOs, 270 | load: dashboard.getLoadAverage, 271 | cpu: dashboard.getCpu, 272 | diskstats: dashboard.getDiskstats, 273 | }; 274 | -------------------------------------------------------------------------------- /public/js/odometer.js: -------------------------------------------------------------------------------- 1 | /*! odometer 0.3.6 */ 2 | (function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y;p='',m=''+p+"",d='8'+m+"",g='',c=",ddd",h=60,f=2e3,a=20,i=2,e=.5,j=1e3/h,b=1e3/a,n="transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd",t=document.createElement("div").style,o=null!=t.transition||null!=t.webkitTransition||null!=t.mozTransition||null!=t.oTransition,s=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,k=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,q=function(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.children[0]},r=function(){var a,b;return null!=(a=null!=(b=window.performance)?b.now():void 0)?a:+new Date},v=!1,(u=function(){var a,b,c,d,e;if(!v&&null!=window.jQuery){for(v=!0,d=["html","text"],e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(function(a){var b;return b=window.jQuery.fn[a],window.jQuery.fn[a]=function(a){return null==a||null==this[0].odometer?b.apply(this,arguments):this[0].odometer.update(a)}}(a));return e}})(),setTimeout(u,0),l=function(){function a(b){var d,e,g,h,k,l,m,n,o,p,q,r,s,t,u,v=this;if(this.options=b,this.el=this.options.el,null!=this.el.odometer)return this.el.odometer;for(this.el.odometer=this,s=a.options,h=o=0,q=s.length;q>o;h=++o)e=s[h],null==this.options[e]&&(this.options[e]=h);this.value=this.cleanValue(null!=(t=this.options.value)?t:""),null==(k=this.options).format&&(k.format=c),(l=this.options).format||(l.format="d"),null==(m=this.options).duration&&(m.duration=f),this.MAX_VALUES=0|this.options.duration/j/i,this.renderInside(),this.render();try{for(u=["HTML","Text"],n=function(a){return Object.defineProperty(v.el,"inner"+a,{get:function(){return v.inside["outer"+a]},set:function(a){return v.update(v.cleanValue(a))}})},p=0,r=u.length;r>p;p++)g=u[p],n(g)}catch(w){d=w,this.watchForMutations()}}return a.prototype.renderInside=function(){return this.inside=document.createElement("div"),this.inside.className="odometer-inside",this.el.innerHTML="",this.el.appendChild(this.inside)},a.prototype.watchForMutations=function(){var a,b=this;if(null!=k)try{return null==this.observer&&(this.observer=new k(function(){var a;return a=b.el.innerText,b.renderInside(),b.render(b.value),b.update(a)})),this.watchMutations=!0,this.startWatchingMutations()}catch(c){a=c}},a.prototype.startWatchingMutations=function(){return this.watchMutations?this.observer.observe(this.el,{childList:!0}):void 0},a.prototype.stopWatchingMutations=function(){var a;return null!=(a=this.observer)?a.disconnect():void 0},a.prototype.cleanValue=function(a){return parseInt(a.toString().replace(/[.,]/g,""),10)||0},a.prototype.bindTransitionEnd=function(){var a,b,c,d,e,f,g=this;if(!this.transitionEndBound){for(this.transitionEndBound=!0,b=!1,e=n.split(" "),f=[],c=0,d=e.length;d>c;c++)a=e[c],f.push(this.el.addEventListener(a,function(){return b?!0:(b=!0,setTimeout(function(){return g.render(),b=!1},0),!0)},!1));return f}},a.prototype.resetFormat=function(){return this.format=this.options.format.split("").reverse().join("")},a.prototype.render=function(a){var b,c,d,e,f,g,h,i,j;for(null==a&&(a=this.value),this.stopWatchingMutations(),this.resetFormat(),this.inside.innerHTML="",b=this.el.className.split(" "),e=[],f=0,h=b.length;h>f;f++)c=b[f],c.length&&(/^odometer(-|$)/.test(c)||e.push(c));for(e.push("odometer"),o||e.push("odometer-no-transitions"),this.options.theme?e.push("odometer-theme-"+this.options.theme):e.push("odometer-auto-theme"),this.el.className=e.join(" "),this.ribbons={},this.digits=[],j=a.toString().split("").reverse(),g=0,i=j.length;i>g;g++)d=j[g],this.addDigit(d);return this.startWatchingMutations()},a.prototype.update=function(a){var b,c=this;return a=this.cleanValue(a),(b=a-this.value)?(this.el.className+=b>0?" odometer-animating-up":" odometer-animating-down",this.stopWatchingMutations(),this.animate(a),this.startWatchingMutations(),setTimeout(function(){return c.el.offsetHeight,c.el.className+=" odometer-animating"},0),this.value=a):void 0},a.prototype.renderDigit=function(){return q(d)},a.prototype.insertDigit=function(a){return this.inside.children.length?this.inside.insertBefore(a,this.inside.children[0]):this.inside.appendChild(a)},a.prototype.addSpacer=function(a){var b;return b=q(g),b.innerHTML=a,this.insertDigit(b)},a.prototype.addDigit=function(a){var b,c,d;if("-"===a)return this.addSpacer("-"),void 0;for(d=!1;;){if("-"===a)break;if(!this.format.length){if(d)throw new Error("Bad odometer format without digits");this.resetFormat(),d=!0}if(b=this.format[0],this.format=this.format.substring(1),"d"===b)break;this.addSpacer(b)}return c=this.renderDigit(),c.querySelector(".odometer-value").innerHTML=a,this.digits.push(c),this.insertDigit(c)},a.prototype.animate=function(a){return o?this.animateSlide(a):this.animateCount(a)},a.prototype.animateCount=function(a){var c,d,e,f,g,h=this;if(d=+a-this.value)return f=e=r(),c=this.value,(g=function(){var i,j,k;return r()-f>h.options.duration?(h.value=a,h.render(),void 0):(i=r()-e,i>b&&(e=r(),k=i/h.options.duration,j=d*k,c+=j,h.render(Math.round(c))),null!=window.requestAnimationFrame?s(g):setTimeout(g,b))})()},a.prototype.animateSlide=function(a){var b,c,d,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y;if(d=a-this.value){for(this.bindTransitionEnd(),f=Math.ceil(Math.log(Math.max(Math.abs(a),Math.abs(this.value))+1)/Math.log(10)),g=[],b=0,l=r=0;f>=0?f>r:r>f;l=f>=0?++r:--r){if(p=Math.floor(this.value/Math.pow(10,f-l-1)),i=Math.floor(a/Math.pow(10,f-l-1)),h=i-p,Math.abs(h)>this.MAX_VALUES){for(k=[],m=h/(this.MAX_VALUES+this.MAX_VALUES*b*e),c=p;h>0&&i>c||0>h&&c>i;)k.push(Math.round(c)),c+=m;k[k.length-1]!==i&&k.push(i),b++}else k=function(){x=[];for(var a=p;i>=p?i>=a:a>=i;i>=p?a++:a--)x.push(a);return x}.apply(this);for(l=s=0,u=k.length;u>s;l=++s)j=k[l],k[l]=Math.abs(j%10);g.push(k)}for(w=g.reverse(),y=[],l=t=0,v=w.length;v>t;l=++t)k=w[l],this.digits[l]||this.addDigit(" "),null==(q=this.ribbons)[l]&&(q[l]=this.digits[l].querySelector(".odometer-ribbon-inner")),this.ribbons[l].innerHTML="",0>d&&(k=k.reverse()),y.push(function(){var a,b,c;for(c=[],n=b=0,a=k.length;a>b;n=++b)j=k[n],o=document.createElement("div"),o.className="odometer-value",o.innerHTML=j,this.ribbons[l].appendChild(o),n===k.length-1&&(o.className+=" odometer-last-value"),0===n?c.push(o.className+=" odometer-first-value"):c.push(void 0);return c}.call(this));return y}},a}(),l.options=null!=(x=window.odometerOptions)?x:{},setTimeout(function(){var a,b,c,d,e;if(window.odometerOptions){d=window.odometerOptions,e=[];for(a in d)b=d[a],e.push(null!=(c=l.options)[a]?(c=l.options)[a]:c[a]=b);return e}},0),l.init=function(){var a,b,c,d,e;for(b=document.querySelectorAll(l.options.selector||".odometer"),e=[],c=0,d=b.length;d>c;c++)a=b[c],e.push(a.odometer=new l({el:a,value:a.innerText}));return e},null!=(null!=(y=document.documentElement)?y.doScroll:void 0)&&null!=document.createEventObject?(w=document.onreadystatechange,document.onreadystatechange=function(){return"complete"===document.readyState&&l.options.auto!==!1&&l.init(),null!=w?w.apply(this,arguments):void 0}):document.addEventListener("DOMContentLoaded",function(){return l.options.auto!==!1?l.init():void 0},!1),window.Odometer=l}).call(this); --------------------------------------------------------------------------------