first commit
This commit is contained in:
commit
1364a13a8d
3
assets/app.css
Normal file
3
assets/app.css
Normal file
@ -0,0 +1,3 @@
|
||||
.loading {
|
||||
cursor: progress;
|
||||
}
|
23
assets/app.js
Normal file
23
assets/app.js
Normal file
@ -0,0 +1,23 @@
|
||||
$(function() {
|
||||
$("#nodeTable tr[data-node]").each(function(){
|
||||
var $this = $(this);
|
||||
var node = $this.data("node");
|
||||
$.getJSON("/status/"+node, function (data) {
|
||||
$this.find("span.loading").hide();
|
||||
var label;
|
||||
if (data["Status"] === "UP") {
|
||||
label = "label-success";
|
||||
$this.addClass("success");
|
||||
$this.find("td.ping").text(data["Ping"] + "ms");
|
||||
} else if (data["Status"] === "SLOW") {
|
||||
label = "label-warning";
|
||||
$this.addClass("warning");
|
||||
$this.find("td.ping").text(data["Ping"] + "ms");
|
||||
} else {
|
||||
label = "label-danger";
|
||||
$this.addClass("danger");
|
||||
}
|
||||
$this.find("span.status").addClass(label).text(data["Status"]);
|
||||
})
|
||||
});
|
||||
})
|
37
html/index.html
Normal file
37
html/index.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="/assets/app.js" type="text/javascript" charset="utf-8"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/assets/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="panel panel-default">
|
||||
<!-- Default panel contents -->
|
||||
<div class="panel-heading">{{.Title}}</div>
|
||||
<table class="table" id="nodeTable">
|
||||
<tr>
|
||||
<th>Host</th>
|
||||
<th>Status</th>
|
||||
<th>Ping</th>
|
||||
</tr>
|
||||
{{range $node := .Nodes}}
|
||||
<tr data-node="{{$node}}">
|
||||
<td class="host">{{$node}}</td>
|
||||
<td>
|
||||
<span class="loading"><i class="icon-spinner icon-spin"></i>Loading...</span>
|
||||
<span class="status label"></span>
|
||||
</td>
|
||||
<td class="ping">
|
||||
-
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
1
nodes.json
Normal file
1
nodes.json
Normal file
@ -0,0 +1 @@
|
||||
["matchbox.vpn", "turingmachine.vpn", "higgsboson.vpn", "devkid-router.vpn", "devkid-desktop.vpn", "devkid-server.vpn", "devkid-nas.vpn"]
|
111
server.go
Normal file
111
server.go
Normal file
@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"html/template"
|
||||
"os/exec"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var indexTemplate = template.Must(template.ParseFiles("html/index.html"))
|
||||
var nodeList = []string{"higgsboson.vpn","turingmachine.vpn"}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type IndexPage struct {
|
||||
Title string
|
||||
Nodes []string
|
||||
}
|
||||
|
||||
type Status int
|
||||
|
||||
const (
|
||||
On Status = iota
|
||||
Off
|
||||
Slow
|
||||
)
|
||||
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
indexTemplate.Execute(w, &IndexPage{Title: "VPN Node Status", Nodes: nodeList})
|
||||
}
|
||||
|
||||
func ping(node string) (int64) {
|
||||
cmd := exec.Command("fping", "-e", node)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err == nil {
|
||||
match := regexp.MustCompile("\\d+").Find(out.Bytes())
|
||||
if (match != nil) {
|
||||
time, _ := strconv.ParseInt(string(match), 10, 0);
|
||||
return time;
|
||||
}
|
||||
return -1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
type NodeStatus struct {
|
||||
Domain string
|
||||
Status string
|
||||
Ping int64
|
||||
}
|
||||
|
||||
const lenPath = len("/status/")
|
||||
func statusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
node := r.URL.Path[lenPath:];
|
||||
if ! stringInSlice(node, nodeList) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
} else {
|
||||
var state string
|
||||
elapsed := ping(node);
|
||||
if elapsed == -1 {
|
||||
state = "DOWN"
|
||||
} else if elapsed > 200 {
|
||||
state = "SLOW"
|
||||
} else {
|
||||
state = "UP"
|
||||
}
|
||||
status := &NodeStatus{Domain: node, Status: state, Ping: elapsed}
|
||||
s, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Printf("error while serializing json %s", err)
|
||||
} else {
|
||||
w.Write(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
config, err := os.Open("./nodes.json")
|
||||
if err != nil {
|
||||
fmt.Printf("Cannot open nodes.json: '%s'", err)
|
||||
os.Exit(1);
|
||||
}
|
||||
jsonParser := json.NewDecoder(config)
|
||||
|
||||
if err = jsonParser.Decode(&nodeList); err != nil {
|
||||
fmt.Printf("parsing config file: '%s'", err.Error())
|
||||
os.Exit(1);
|
||||
}
|
||||
|
||||
http.HandleFunc("/status/", statusHandler)
|
||||
http.Handle("/assets/", http.StripPrefix("/assets", http.FileServer(http.Dir("./assets/"))))
|
||||
http.HandleFunc("/", indexHandler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
fmt.Printf("Running on 8080")
|
||||
}
|
Loading…
Reference in New Issue
Block a user