cauto 2 роки тому
батько
коміт
6424f0b243
21 змінених файлів з 4884 додано та 125 видалено
  1. 78 0
      funcs/ping.go
  2. 24 2
      go.mod
  3. 114 0
      go.sum
  4. 1216 0
      html/add.html
  5. BIN
      html/alert.mp3
  6. 264 0
      html/alerts.html
  7. 1216 0
      html/config.html
  8. BIN
      html/favicon.ico
  9. 350 0
      html/index.html
  10. 326 0
      html/mapping.html
  11. 362 0
      html/reverse.html
  12. 185 0
      html/tools.html
  13. 276 0
      html/topology.html
  14. 25 59
      main.go
  15. 126 0
      nettools/dns.go
  16. 151 0
      nettools/https.go
  17. 2 2
      nettools/icmp_unix.go
  18. 41 41
      nettools/icmpping.go
  19. 0 1
      nettools/pingI.go
  20. 20 20
      nettools/tcpping.go
  21. 108 0
      nettools/tls.go

+ 78 - 0
funcs/ping.go

@@ -0,0 +1,78 @@
+package funcs
+
+import (
+	"github.com/cihub/seelog"
+	"net"
+	"node_monitor/g"
+	"node_monitor/nettools"
+	"sync"
+	"time"
+)
+
+func Ping() {
+	//	var wg sync.WaitGroup
+	//for _, target := range g.SelfCfg.Ping {
+	//	wg.Add(1)
+	//	go PingTask(g.Cfg.Network[target], &wg)
+	//}
+	//wg.Wait()
+	//go StartAlert()
+}
+
+func PingTask(t string, wg *sync.WaitGroup) {
+	//var ipSlice []string
+	//ipSlice = append(ipSlice, "kdvkr-02.xyz")
+	//ipSlice = append(ipSlice, "kdvkr-04.xyz")
+	stat := g.PingSt{}
+	stat.MinDelay = -1
+	lossPK := 0
+	addr, err := net.ResolveIPAddr("ip", t)
+	seelog.Info("Start Ping " + t + "..")
+	if err == nil {
+		for i := 0; i < 10; i++ {
+			starttime := time.Now().UnixNano()
+			//tcpping := nettools.NewTcpPing(addr.String(), 22, time.Second*4)
+			icmping := nettools.NewIcmpPing(addr.String(), time.Second*4)
+			rest := icmping.Ping()
+			if rest.Error() == nil {
+				delay := rest.Result()
+				seelog.Info("[func:IcmpPing] Finish Addr:", addr, delay, "ms")
+				stat.AvgDelay = stat.AvgDelay + rest.Result()
+				if stat.MaxDelay < delay {
+					stat.MaxDelay = delay
+				}
+				if stat.MinDelay == -1 || stat.MinDelay > delay {
+					stat.MinDelay = delay
+				}
+				stat.RevcPk = stat.RevcPk + 1
+
+				stat.SendPk = stat.SendPk + 1
+				stat.LossPk = int((float64(lossPK) / float64(stat.SendPk)) * 100)
+				duringtime := time.Now().UnixNano() - starttime
+				time.Sleep(time.Duration(3000*1000000-duringtime) * time.Nanosecond)
+			} else {
+				seelog.Debug("[func:StartPing IcmpPing] ID:", i, " IP:", addr, "| err:", rest.Error())
+				lossPK = lossPK + 1
+			}
+
+		}
+		if stat.RevcPk > 0 {
+			stat.AvgDelay = stat.AvgDelay / stat.RevcPk
+		} else {
+			stat.AvgDelay = 0.0
+		}
+		seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " MaxDelay:", stat.MaxDelay, " MinDelay:", stat.MinDelay, " AvgDelay:", stat.AvgDelay, " Revc:", stat.RevcPk, " LossPK:", stat.LossPk)
+	} else {
+		stat.AvgDelay = 0.00
+		stat.MinDelay = 0.00
+		stat.MaxDelay = 0.00
+		stat.SendPk = 0
+		stat.RevcPk = 0
+		stat.LossPk = 100
+		seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " Unable to resolve destination host")
+	}
+	//for i := 0; i < len(ipSlice); i++ {
+	//
+	//
+	//}
+}

+ 24 - 2
go.mod

@@ -2,9 +2,31 @@ module node_monitor
 
 go 1.19
 
-require golang.org/x/net v0.1.0
+require (
+	github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575
+	github.com/gin-gonic/gin v1.8.1
+	github.com/miekg/dns v1.1.50
+	golang.org/x/net v0.1.0
+)
 
 require (
-	github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-playground/locales v0.14.0 // indirect
+	github.com/go-playground/universal-translator v0.18.0 // indirect
+	github.com/go-playground/validator/v10 v10.10.0 // indirect
+	github.com/goccy/go-json v0.9.7 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/mattn/go-isatty v0.0.14 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.1 // indirect
+	github.com/ugorji/go/codec v1.2.7 // indirect
+	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	golang.org/x/tools v0.1.12 // indirect
+	google.golang.org/protobuf v1.28.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 )

+ 114 - 0
go.sum

@@ -1,6 +1,120 @@
 github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs=
 github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
+github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
+github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
+github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
+github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
+github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
+github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
+github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
+github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 1216 - 0
html/add.html

@@ -0,0 +1,1216 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+    <div class="navbar-inner">
+        <div class="container">
+          <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+          <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+            <div class="nav-collapse">
+            <!--
+            <ul class="nav pull-right">
+                <li class="dropdown">
+                    <a target="_blank" href="http://smartping.cn/" >
+                        <i class="icon-comment"></i>
+                        Know About SmartPing.CN ?
+                    </a>
+                </li>
+            </ul>
+            -->
+        </div>
+            <!--/.nav-collapse -->
+        </div>
+        <!-- /container -->
+    </div>
+    <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li ><a   href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+          <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+          <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+          <li class="active"><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main localmode" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container" id="main">
+      <div class="row">
+          <div class="span4">
+            <div class="widget-header">
+              <i class="icon-user"></i>
+              <h3>保存配置 </h3>
+               <span style="float: right;margin-right: 25px;"><a  target="_blank" href="/api/config.json"><i class="icon-file"></i></a></span>
+            </div> <!-- /widget-header -->
+            <div class="widget-content">
+              <div style="text-align: center">
+                <span>
+                  <b>密码:</b>
+                  <input style="width:120px;" class="password" type="password" id="pass">
+                  <button style="margin: -10px 0 0 0;" type="submit" class="btn btn-primary" onclick="saveconf()">保存</button>
+                </span>
+                <div style="text-align: center"><a href="?dcloud"><i class="icon-cloud"></i> 切换到云模式 </a></div>
+              </div>
+
+            </div>
+            <br/>
+            <div class="widget-header">
+              <i class="icon-user"></i>
+              <h3>基础配置</h3>
+            </div> <!-- /widget-header -->
+
+            <div class="widget-content">
+
+            <h4>基础</h4><br>
+
+              <div class="control-group"  style="float: left;width: 28%;">
+                <label class="control-label" >接口超时(秒)</label>
+                <div class="controls">
+                  <input type="text" style="width:100%" id="Timeout" value="" >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+              <div class="control-group"  style="float: left;width: 28%;margin-left: 20px;">
+                <label class="control-label" for="Refresh">页面刷新(分钟)</label>
+                <div class="controls">
+                  <input type="text"   style="width:100%" id="Refresh" value=""  >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+
+              <div class="control-group"  style="width: 28%;float: left;margin-left: 20px;">
+                <label class="control-label ttip" for="Archive" data-toggle='tooltip' title='此配置仅针对全国延迟与报警日志数据生效,Ping数据默认保存30天不可修改!'  data-placement='bottom'>数据存档(天)</label>
+                <div class="controls">
+                  <input type="text" style="width:100%"   id="Archive" value=""  >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+            <hr style="clear:both;">
+            <h4>Ping拓扑</h4><br>
+
+            <div class="control-group" style="float: left;width: 43%;">
+              <label class="control-label" for="Tsound">报警声音</label>
+              <div class="controls">
+                <input type="text"  style="width:97%" id="Tsound" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+            <div class="control-group"  style="width: 20%;float: left;margin-left: 20px;">
+              <label class="control-label" for="Tline">连线粗细</label>
+              <div class="controls">
+                <input type="text"  style="width:100%"  id="Tline" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+            <div class="control-group"  style="width: 20%;float: left;margin-left: 20px;">
+              <label class="control-label" for="Tsymbolsize">形状大小</label>
+              <div class="controls">
+                <input type="text" style="width:100%"   id="Tsymbolsize" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+           <hr style="clear:both;">
+           <h4>报警邮件<button style="float: right" onclick="sendMailTest(this)">发送测试邮件</button></h4><br>
+            <div class="control-group" >
+                <label class="control-label" for="Tsymbolsize">邮件服务器</label>
+                <div class="controls">
+                    <input type="text" style="width:97%"   id="EmailHost" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group"  style="width: 46%;float: left;">
+                <label class="control-label" for="Tsymbolsize">发件邮件</label>
+                <div class="controls">
+                    <input type="text" style="width:100%"   id="SendEmailAccount" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group"  style="width: 46%;float: left;margin-left: 20px;">
+                <label class="control-label" for="Tsymbolsize">发件邮箱密码</label>
+                <div class="controls">
+                    <input type="password" style="width:100%"   id="SendEmailPassword" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group" >
+                <label class="control-label" for="Tsymbolsize">收件邮箱列表(分号隔开)</label>
+                <div class="controls">
+                    <input type="text" style="width:97%"   id="RevcEmailList" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+
+              <hr style="clear:both;">
+
+              <h4>检测工具</h4><br>
+
+              <div class="control-group"  style="float: left;width: 45%;">
+                <label class="control-label" for="Toollimit">限定频率(秒)</label>
+                <div class="controls">
+                  <input type="text"   style="width:100%" id="Toollimit" value="">
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+            <hr style="clear:both;">
+            <h4>授权管理</h4><br>
+            <div class="control-group"  style="width: 96%;">
+                <label class="control-label" for="Authiplist">用户IP列表(逗号隔开)</label>
+                <div class="controls">
+                    <input type="text" style="width:100%"  id="Authiplist" value="" >
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <br/>
+
+          </div>
+          </div>
+          <div class="span8">
+            <!-- /widget -->
+            <div class="widget widget-table action-table">
+                <div class="widget-header"> <i class="icon-th-list"></i>
+                  <h3>Ping节点测试网络</h3>
+                </div>
+                <!-- /widget-header -->
+                <div class="widget-content">
+                  <table class="table table-striped table-bordered">
+                    <thead>
+                      <tr>
+                        <th> 节点名称(本机) </th>
+                        <th> 节点IP(本机) </th>
+                        <th> SmartPing </th>
+                        <th style="width:60px;"> 正向Ping </th>
+                        <th style="width:60px;text-align: center">Ping拓扑</th>
+                        <!--<th style="width:60px;text-align: center">检测工具</th>-->
+                        <th  style="width:15px;">  </th>
+                      </tr>
+                    </thead>
+                    <tbody  class="selfagent"></tbody>
+                    <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:60px;"> SmartPing </th>
+                        <th style="width:60px;"> 正向Ping </th>
+                        <th style="width:60px;text-align: center">Ping拓扑</th>
+                        <!--<th style="width:60px;text-align: center">检测工具</th>-->
+                        <th  style="width:15px;">  </th>
+                      </tr>
+                    </thead>
+                    <tbody class="agentlist"></tbody>
+                    <tfoot>
+                      <tr>
+                        <td colspan="8" style="text-align: center"><input style='margin:4px 3px 0 0;' type='button' class='btn btn-small' value='添加节点' data-toggle="modal" data-target="#agentModal"></td>
+                      </tr>
+                    </tfoot>
+                  </table>
+                </div>
+                <!-- /widget-content -->
+              </div>
+
+            <!-- editSelfAgentModal -->
+            <div class="modal fade" id="editSelfagentModal" tabindex="-1" role="dialog"  style="width:480px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">编辑本机节点</h4>
+                  </div>
+                  <div class="modal-body form-horizontal" >
+                    节点名称:<input id="editAgentName" type="text" style="width: 150px;"> 节点IP: <input id="editAgentIp" type="text" style="width: 150px;">
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="editSelfagentModal(true)">暂存</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- NewAgentModal -->
+            <div class="modal fade" id="agentModal" tabindex="-1" role="dialog"  style="width:480px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">新增节点</h4>
+                  </div>
+                  <div class="modal-body form-horizontal" >
+                    节点名称:<input id="newAgentName" type="text" style="width: 150px;"> 节点IP: <input id="newAgentIp" type="text" style="width: 150px;">
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="addAgent(null,null)">暂存</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- PingModal -->
+            <div class="modal fade" id="pingModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:500px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">Ping配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="text-align: center"> 正向Ping <input type='checkbox' class="allchecked"></th>
+                      </tr>
+                      </thead>
+                      <tbody class="pingModalist"></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="pingcfg(null,true)" >暂存</button>
+                    <input type='hidden' id='pingcfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- TopoModal -->
+            <div class="modal fade" id="topoModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:800px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="myModalLabel">拓扑配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:70px;"> 正向拓扑 <input type='checkbox' class="allchecked" style="margin-top: 2px"  v="topoCheck" /></th>
+                        <th style="width:450px;"> 报警规则 </th>
+                      </tr>
+                      </thead>
+                      <tbody class="topoModallist"></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="topocfg(null,true)">暂存</button>
+                    <input type='hidden' id='topocfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- ToolsModal
+            <div class="modal fade" id="toolModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:800px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">工具配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:450px;"> 可用工具 </th>
+                      </tr>
+                      </thead>
+                      <tbody class="toolModallist" ></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="toolcfg(null,true)">暂存</button>
+                    <input type='hidden' id='toolscfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+            -->
+
+            <!-- /全国延迟测试网络 -->
+            <div class="widget widget-table action-table">
+              <div class="widget-header"> <i class="icon-th-list"></i>
+                <h3>全国延迟测试网络</h3>
+              </div>
+              <!-- /widget-header -->
+              <div class="widget-content chinamaplist" style="padding:10px;"></div>
+            <!-- /widget -->
+            </div>
+
+            <!-- chinamapModal -->
+            <div class="modal fade" id="addChinaMapModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:550px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" >新增全国延迟测试节点</h4>
+                  </div>
+                  <div class="modal-body ">
+                    <div style="text-align: center"><select class='provSelect' style='width:70px;'><option value='北京'>北京</option><option value='浙江'>浙江</option><option value='天津'>天津</option><option value='安徽'>安徽</option><option value='上海'>上海</option><option value='福建'>福建</option><option value='重庆'>重庆</option><option value='江西'>江西</option><option value='山东'>山东</option><option value='河南'>河南</option><option value='湖北'>湖北</option><option value='湖南'>湖南</option><option value='广东'>广东</option><option value='海南'>海南</option><option value='山西'>山西</option><option value='青海'>青海</option><option value='江苏'>江苏</option><option value='辽宁'>辽宁</option><option value='吉林'>吉林</option><option value='台湾'>台湾</option><option value='河北'>河北</option><option value='贵州'>贵州</option><option value='四川'>四川</option><option value='云南'>云南</option><option value='陕西'>陕西</option><option value='甘肃'>甘肃</option><option value='黑龙江'>黑龙江</option><option value='香港'>香港</option><option value='澳门'>澳门</option><option value='广西'>广西</option><option value='宁夏'>宁夏</option><option value='新疆'>新疆</option><option value='内蒙古'>内蒙古</option><option value='西藏'>西藏</option></select></div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 电信 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_ctcc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_ctcc')">
+                    </div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 联通 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_cucc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_cucc')">
+                    </div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 移动 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_cmcc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_cmcc')">
+                    </div>
+                  </div>
+                </div>
+                <div class="modal-footer">
+                  <button type="button" class="btn btn-primary" onclick="addChinaMap(true)">暂存</button>
+                </div>
+              </div>
+            </div>
+
+
+            <!-- chinamapModal -->
+            <div class="modal fade" id="chinaMapModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:550px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title editChinaMapProv" ></h4>
+                  </div>
+                  <div class="modal-body ">
+                    <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                            <tr>
+                              <th> 电信 </th>
+                            </tr>
+                          </thead>
+                          <tbody class="chinamap_ctcc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_ctcc')">
+                      </div>
+                      <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                          <tr>
+                            <th> 联通 </th>
+                          </tr>
+                          </thead>
+                          <tbody class="chinamap_cucc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_cucc')">
+                      </div>
+                      <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                          <tr>
+                            <th> 移动 </th>
+                          </tr>
+                          </thead>
+                          <tbody class="chinamap_cmcc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_cmcc')">
+                      </div>
+                  </div>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-danger pull-left" onclick="delChinaMap()">删除</button>
+                    <button type="button" class="btn btn-primary" onclick="editChinaMap(null,true)">暂存</button>
+                  </div>
+                </div>
+              </div>
+
+
+          </div>
+          </div>
+      <hr>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+<div class="main cloudmode" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span3"> &nbsp;</div>
+        <div class="span6">
+            <div class="widget-content">
+                <div id="LastSuccTimeStr" style="text-align: center"></div>
+                <div class="control-group">
+              <label class="control-label">云配置地址</label>
+              <div class="controls">
+                <div class="input-append">
+                  <input  style="width:98%" class="m-wrap" id="Endpoint" value="http://" type="text">
+                </div>
+              </div>	<!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="row">
+              <div class="span2" style="width: 44.4%;">
+                <div class="control-group">
+                  <label class="control-label">节点名称(本机)</label>
+                  <div class="controls">
+                    <input class="cpassword" type="text" style="width:97%"  id="CloudSelfName" value="">
+                  </div> <!-- /controls -->
+                </div> <!-- /control-group -->
+              </div>
+              <div class="span2" style="width: 44.4%;">
+                <div class="control-group">
+                  <label class="control-label">节点IP(本机)</label>
+                  <div class="controls">
+                    <input class="cpassword" type="text" style="width:97%"  id="CloudSelfAddr" value="">
+                  </div> <!-- /controls -->
+                </div> <!-- /control-group -->
+              </div>
+            </div>
+            <div class="control-group">
+              <label class="control-label">密码</label>
+              <div class="controls">
+                <input class="cpassword" type="password" style="width:97%"  id="CloudPass" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <button style="width:100%"  type="submit" class="btn btn-primary" onclick="savecloudconf()">检测并保存</button>
+            <div style="text-align: center;margin-top: 10px;"><a  href="?local"><i class="icon-home"></i> 切换到本地模式 </a></div>
+          </div>
+        </div>
+        <div class="span3">&nbsp;</div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/jquery.cookie.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    var tp = window.location.search;
+    //console.log(tp)
+    if(tp=="?cloud" || tp=="?dcloud" ){
+        $(".localmode").hide();
+        $(".cloudmode").show();
+    }
+    if(tp=="?local"){
+        $(".localmode").show();
+        $(".cloudmode").hide();
+    }
+    //Ping节点测试网络 - 编辑本机节点
+    function editSelfagentModal(Save){
+        if(Save){
+            editAgentName = $("#editAgentName").val();
+            editAgentAddr = $("#editAgentIp").val();
+            config=$.parseJSON(sessionStorage.getItem("config"))
+            config["Name"]=editAgentName
+            config["Addr"]=editAgentAddr
+            sessionStorage.setItem("config",JSON.stringify(config))
+            delAgent(null,$(".selfAgentAddr").html())
+            $(".selfAgentName").html(editAgentName)
+            $(".selfAgentAddr").html(editAgentAddr)
+            addAgent(editAgentName,editAgentAddr)
+            config["Network"][editAgentAddr]["Smartping"]=true
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#editSelfagentModal').modal("hide");
+            return
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        $("#editAgentName").val(config["Name"]);
+        $("#editAgentIp").val(config["Addr"]);
+        $('#editSelfagentModal').modal();
+    }
+    //Ping节点测试网络 - 新增节点
+    function addAgent(Name,Ip){
+        if(Name==null && Ip==null){
+            newAgentName = $("#newAgentName").val();
+            newAgentIp = $("#newAgentIp").val();
+        }else{
+            newAgentName = Name
+            newAgentIp = Ip
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        //检测节点IP或Name是否重复
+        CheckFlag=""
+        $.each(config["Network"],function(Addr,network){
+            if(Addr==newAgentIp || network["Addr"]==newAgentIp){
+                CheckFlag = "节点IP不能重复!"
+                return false
+            }
+            if( network["Name"]==newAgentName){
+                CheckFlag = "节点名称不能重复!"
+                return false
+            }
+        })
+        if(CheckFlag!=""){
+            alert(CheckFlag)
+            return
+        }
+        //添加新节点
+        if(Name==null && Ip==null){
+            $(".agentlist").append("<tr class='agentlist-"+newAgentIp+"'>" +
+                "<td>"+newAgentName+"</td><td class='Addr'>"+newAgentIp+"</td>" +
+                "<td style='text-align:center'><input type='checkbox' onclick='checkSP(this)'></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='pingcfg(this,null)' disabled='disabled'>配置</a></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='topocfg(this,false)' disabled='disabled'>配置</a></td>" +
+                //"<td style='text-align:center'><a class='btn btn-mini' onclick='toolcfg(this,null)' disabled='disabled'>配置</a></td>" +
+                "<td><i onclick='delAgent(this,null);' class='icon-large icon-trash'></i></td>" +
+                "</tr>")
+        }
+        agent=new Object()
+        agent["Name"] = newAgentName
+        agent["Addr"]=newAgentIp
+        agent["Ping"]=new Array()
+        //agent["Tools"]=new Object()
+        //agent["Tools"]["Icmpping"]=new Array()
+        agent["Topology"]=new Array()
+        /*
+        $.each(config["Network"], function(Addr, agentconfig){
+            agent["Ping"].push(Addr)
+            agent["Tools"]["Icmpping"].push(Addr)
+            topoMap = new Map()
+            topoMap["Name"]=agentconfig["Name"]
+            topoMap["Addr"]=agentconfig["Addr"]
+            topoMap["Thdchecksec"]="0"
+            topoMap["Thdoccnum"]="0"
+            topoMap["Thdavgdelay"]="0"
+            topoMap["Thdloss"]="0"
+            agent["Topology"].push(topoMap)
+        })
+        */
+        config["Network"][newAgentIp]=agent
+       //每个已有节点下加入新节点
+        /*
+        $.each(config["Network"], function(Addr, obj){
+            config["Network"][Addr]["Ping"].push(newAgentIp);
+            config["Network"][Addr]["Tools"]["Icmpping"].push(newAgentIp);
+            NewtopoMap = new Map()
+            NewtopoMap["Name"]=newAgentName
+            NewtopoMap["Addr"]=newAgentIp
+            NewtopoMap["Thdchecksec"]="0"
+            NewtopoMap["Thdoccnum"]="0"
+            NewtopoMap["Thdavgdelay"]="0"
+            NewtopoMap["Thdloss"]="0"
+            config["Network"][Addr]["Topology"].push(NewtopoMap)
+        })
+        */
+        config["Network"][config["Addr"]]["Ping"].push(newAgentIp);
+        //config["Network"][config["Addr"]]["Tools"]["Icmpping"].push(newAgentIp);
+        NewtopoMap = new Map()
+        NewtopoMap["Name"]=newAgentName
+        NewtopoMap["Addr"]=newAgentIp
+        NewtopoMap["Thdchecksec"]="900"
+        NewtopoMap["Thdoccnum"]="3"
+        NewtopoMap["Thdavgdelay"]="200"
+        NewtopoMap["Thdloss"]="30"
+        config["Network"][config["Addr"]]["Topology"].push(NewtopoMap)
+        sessionStorage.setItem("config",JSON.stringify(config))
+        $("#newAgentName").val("")
+        $("#newAgentIp").val("")
+        $('#agentModal').modal('hide');
+    }
+    //Ping节点测试网络 - 删除节点
+    function delAgent(obj,SelfAddr){
+        if(SelfAddr!=null){
+            Addr=SelfAddr
+        }else{
+            Addr=$(obj).parent().parent().find(".Addr").html()
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        delete config["Network"][Addr];
+        $.each(config["Network"], function(iAddr, agentconfig){
+            $.each(agentconfig["Ping"], function(pingIndex, pingAddr){
+                if(pingAddr==Addr){
+                    config["Network"][iAddr]["Ping"].splice(pingIndex,1)
+                    return false
+                }
+            })
+            /*
+            $.each(agentconfig["Tools"]["Icmpping"], function(toolIcmpIndex, toolIcmpAddr){
+                if(toolIcmpAddr==Addr){
+                    config["Network"][iAddr]["Tools"]["Icmpping"].splice(toolIcmpIndex,1)
+                    return false
+                }
+            })
+            */
+            $.each(agentconfig["Topology"], function(topoIndex, topoObj){
+                if(topoObj["Addr"]==Addr){
+                    config["Network"][iAddr]["Topology"].splice(topoIndex,1)
+                    return false
+                }
+            })
+        })
+        sessionStorage.setItem("config",JSON.stringify(config))
+        $(obj).parent().parent().remove();
+    }
+    //Ping节点测试网络 - 正向PING配置弹窗
+    function pingcfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        $(".allchecked").removeAttr("checked")
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save==true){
+            nAddr=$("#pingcfgAddr").val()
+            NewPingList = new Array()
+            $('.pingModalist tr').each(function(i){
+                if($(this).children('td').children('input').is(":checked")){
+                    NewPingList.push($(this).find(".pingModalistAddr").html())
+                }
+            });
+            config["Network"][nAddr]["Ping"]=NewPingList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#pingModal').modal("hide")
+            return
+        }
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        selfconfig=config["Network"][Addr];
+        $(".pingModalist").html("")
+        $.each(config["Network"], function(iAddr, agentconfig){
+            checked=""
+            $.each(selfconfig["Ping"], function(i, ip){
+                if(agentconfig["Addr"]==ip){
+                    checked="checked"
+                    return false
+                }
+            })
+            $(".pingModalist").append("<tr>" +
+                "<td>"+agentconfig["Name"]+"</td>" +
+                "<td class='pingModalistAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  class='form-horizontal' style='text-align: center'><input type='checkbox' style='margin-top: -3px' "+checked+"></td>" +
+             "</tr>")
+        })
+        $("#pingcfgAddr").val(Addr)
+        $('#pingModal').modal()
+    }
+    //Ping节点测试网络 - 拓扑配置弹窗
+    function topocfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        $(".allchecked").removeAttr("checked")
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save==true){
+            mnAddr=$("#topocfgAddr").val()
+            NewTopoList = new Array()
+            $('.topoModallist tr').each(function(i){
+                //console.log($(this).find(".topoModalListChecked").is(":checked"))
+                if($(this).find(".topoModalListChecked").is(":checked")){
+                    topoModal = new Map()
+                    topoModal["Name"] = $(this).find(".topoModalListName").html()
+                    topoModal["Addr"] = $(this).find(".topoModalListAddr").html()
+                    topoModal["Thdchecksec"] = $(this).find(".topoModalListThdchecksec").val()
+                    topoModal["Thdoccnum"] = $(this).find(".topoModalListThdoccnum").val()
+                    topoModal["Thdavgdelay"] = $(this).find(".topoModalListThdavgdelay").val()
+                    topoModal["Thdloss"] = $(this).find(".topoModalListThdloss").val()
+                    NewTopoList.push(topoModal)
+                }
+            })
+            config["Network"][mnAddr]["Topology"]=NewTopoList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#topoModal').modal("hide")
+            return
+        }
+        mAddr=$(obj).parent().parent().find(".Addr").html()
+        $(".topoModallist").html("")
+        $.each(config["Network"], function(Addr, agentconfig){
+            var itopocfg= new Map()
+            itopocfg["Thdchecksec"] = 0
+            itopocfg["Thdoccnum"] = 0
+            itopocfg["Thdavgdelay"] = 0
+            itopocfg["Thdloss"] = 0
+            checked=""
+            disabled="disabled"
+            if(config["Network"].hasOwnProperty(mAddr)){
+                nodeConfig=config["Network"][mAddr]
+                $.each(nodeConfig["Topology"], function(i, cf){
+                    if(cf["Addr"]==Addr){
+                        itopocfg = cf
+                        checked="checked"
+                        disabled=""
+                        return false
+                    }
+                });
+            }
+            $(".topoModallist").append("<tr>" +
+                "<td class='topoModalListName'>"+agentconfig["Name"]+"</td>" +
+                "<td class='topoModalListAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  style='text-align:center'><input class='topoModalListChecked' type='checkbox' "+checked+" onclick='enabledTopo(this)' ></td>" +
+                "<td class='form-horizontal'>" +
+                "<div class='controls' style='margin-left:0px;'>" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdchecksec' type='text' style='width:25px' value='"+itopocfg["Thdchecksec"]+"' "+disabled+" >" +
+                "<span class='add-on'>秒</span>" +
+                "</div>" +
+                "内出现" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdoccnum' type='text' style='width:20px' value='"+itopocfg["Thdoccnum"]+"' "+disabled+" >" +
+                "<span class='add-on'>次</span>" +
+                "</div>" +
+                "内延迟大于" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdavgdelay' type='text' value='"+itopocfg["Thdavgdelay"]+"' style='width:25px' "+disabled+" >" +
+                "<span class='add-on'>ms</span>" +
+                "</div>" +
+                "或丢包率大于" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdloss' type='text'  value='"+itopocfg["Thdloss"]+"' style='width:20px' "+disabled+" >" +
+                "<span class='add-on'>%</span>" +
+                "</div>" +
+                "</div>" +
+                "</td>" +
+                "</tr>")
+        })
+        $("#topocfgAddr").val(mAddr)
+        $('#topoModal').modal()
+    }
+    function enabledTopo(obj){
+        if($(obj).is(":checked")){
+            $(obj).closest("tr").find(".topoModalListThdchecksec").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdchecksec").val("900")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").val("3")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").val("200")
+            $(obj).closest("tr").find(".topoModalListThdloss").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdloss").val("30")
+        }else{
+            $(obj).closest("tr").find(".topoModalListThdchecksec").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdchecksec").val("0")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").val("0")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").val("0")
+            $(obj).closest("tr").find(".topoModalListThdloss").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdloss").val("0")
+        }
+    }
+    /*/Ping节点测试网络 - 检测工具配置
+    function toolcfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save){
+            nAddr=$("#toolscfgAddr").val()
+            NewToolsIcmpList = new Array()
+            $('.toolModallist tr').each(function(i){
+                if($(this).children('td').children('input').is(":checked")){
+                    NewToolsIcmpList.push($(this).find(".toolModalistAddr").html())
+                }
+            });
+            config["Network"][nAddr]["Tools"]["Icmpping"]=NewToolsIcmpList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#toolModal').modal("hide")
+            return
+        }
+        $(".toolModallist").html("")
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        selfconfig=config["Network"][Addr]
+        $.each(config["Network"], function(iAddr, agentconfig){
+            checked=""
+            $.each(selfconfig["Tools"]["Icmpping"], function(i, ip){
+                if(agentconfig["Addr"]==ip){
+                    checked="checked"
+                    return false
+                }
+            })
+            $(".toolModallist").append("<tr>" +
+                "<td>"+agentconfig["Name"]+"</td>" +
+                "<td class='toolModalistAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  class='form-horizontal' style='text-align: left'><input type='checkbox' style='margin-top: -3px' "+checked+">ICMP PING</td>" +
+                "</tr>")
+        })
+        $('#toolscfgAddr').val(Addr)
+        $('#toolModal').modal()
+    }
+    */
+    //发送邮件测试
+    function sendMailTest(obj){
+        $(obj).html("测试邮件发送中(最长1分钟,请等待)...")
+        data = {}
+        data["EmailHost"] = $("#EmailHost").val();
+        data["SendEmailAccount"] = $("#SendEmailAccount").val();
+        data["SendEmailPassword"] = $("#SendEmailPassword").val();
+        data["RevcEmailList"] = $("#RevcEmailList").val();
+        $.ajax({
+            dataType: "json",
+            type: 'POST',
+            url: "/api/sendmailtest.json",
+            data: data,
+            success: function( retdata ) {
+                if (retdata["status"]=="true"){
+                    alert("测试邮件发送成功!")
+                }else{
+                    alert("测试邮件发送失败 ("+retdata["info"]+")")
+                }
+                $(obj).html("发送测试邮件")
+            },
+            timeout: 65*1000
+        }).fail( function( xhr, status ) {
+            alert("测试邮件发送失败 (API错误!)")
+            $(obj).html("发送测试邮件")
+        });
+    }
+    //全国延迟测试网络-新增节点
+    function addChinaMap(Save){
+        if(Save){
+            nprov=$('.provSelect').attr('value')
+            cmap=new Map()
+            cucc=new Array()
+            $(".add_chinamap_cucc").children('tr').children('td').children('.val').each(function(i,ip){
+                cucc.push($(ip).val())
+            })
+            cmap["cucc"]=cucc
+            ctcc=new Array()
+            $(".add_chinamap_ctcc").children('tr').children('td').children('.val').each(function(i,ip){
+                ctcc.push($(ip).val())
+            })
+            cmap["ctcc"]=ctcc
+            cmcc=new Array()
+            $(".add_chinamap_cmcc").children('tr').children('td').children('.val').each(function(i,ip){
+                cmcc.push($(ip).val())
+            })
+            cmap["cmcc"]=cmcc
+            config["Chinamap"][nprov]=cmap
+            $('#addChinaMapModal').modal("hide")
+            sessionStorage.setItem("config",JSON.stringify(config))
+            refreshChinaMap()
+            return
+        }
+        $.each(config["Chinamap"], function(prov, detail){
+            $(".provSelect option[value='"+prov+"']").remove();
+        })
+        $(".add_chinamap_cucc").find("tr").remove()
+        $(".add_chinamap_ctcc").find("tr").remove()
+        $(".add_chinamap_cmcc").find("tr").remove()
+        $('#addChinaMapModal').modal()
+    }
+    //全国延迟测试网络-删除节点
+    function delChinaMap(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        nprov=$(".editChinaMapProv").html()
+        delete config["Chinamap"][nprov]
+        $('#chinaMapModal').modal("hide")
+        sessionStorage.setItem("config",JSON.stringify(config))
+        refreshChinaMap()
+        return
+    }
+    //全国延迟测试网络-编辑节点
+    function editChinaMap(prov,Save){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save){
+            nprov=$(".editChinaMapProv").html()
+            cmap=new Map()
+            cucc=new Array()
+            $(".chinamap_cucc").children('tr').children('td').children('.val').each(function(i,ip){
+                cucc.push($(ip).val())
+            })
+            cmap["cucc"]=cucc
+            ctcc=new Array()
+            $(".chinamap_ctcc").children('tr').children('td').children('.val').each(function(i,ip){
+                ctcc.push($(ip).val())
+            })
+            cmap["ctcc"]=ctcc
+            cmcc=new Array()
+            $(".chinamap_cmcc").children('tr').children('td').children('.val').each(function(i,ip){
+                cmcc.push($(ip).val())
+            })
+            cmap["cmcc"]=cmcc
+            config["Chinamap"][nprov]=cmap
+            $('#chinaMapModal').modal("hide")
+            sessionStorage.setItem("config",JSON.stringify(config))
+            refreshChinaMap()
+            return
+        }
+        $(".editChinaMapProv").html(prov)
+        $(".chinamap_cmcc").html("")
+        $(".chinamap_ctcc").html("")
+        $(".chinamap_cucc").html("")
+        detail = config["Chinamap"][prov]
+        $.each(detail["cmcc"],function(i,cmcc){
+            $(".chinamap_cmcc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+cmcc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $.each(detail["ctcc"],function(i,ctcc){
+            $(".chinamap_ctcc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+ctcc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $.each(detail["cucc"],function(i,cucc){
+            $(".chinamap_cucc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+cucc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $('#chinaMapModal').modal()
+    }
+    //全国延迟测试网络-刷新面板
+    function refreshChinaMap(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        $(".chinamaplist").html("")
+        $.each(config["Chinamap"], function(prov, detail){
+            cmcc=detail["cmcc"].length
+            ctcc=detail["ctcc"].length
+            cucc=detail["cucc"].length
+            text=prov+"(电信"+ctcc+",联通"+cucc+",移动"+cmcc+")"
+            $(".chinamaplist").append("<input type='button' value='"+text+"' onclick='editChinaMap(\""+prov+"\")' style='margin-right: 10px;width:177px;'>")
+        })
+        $(".chinamaplist").append("<input type='button' value=' + ' style='width: 50px;' onclick='addChinaMap(false)'> ")
+    }
+    //全国延迟测试网络-添加IP条目
+    function addChinaMapCell(cell){
+        $("."+cell).append("<tr><td><input  class='val' type='text' style='width: 108px' value=''>  <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+    }
+    //全国延迟测试网络-删除IP条目
+    function deletedMapingTr(nowTr){
+        $(nowTr).parent().parent().remove();
+    }
+    //保存配置
+    function saveconf(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        //config = {}
+        config["Name"] = $("#selfAgentName").html();
+        config["Addr"] = $("#selfAgentAddr").html();
+        config["Mode"]["Type"]="local"
+        config["Mode"]["Endpoint"]=""
+        config["Mode"]["LastSuccTime"]=""
+        config["Base"]["Timeout"]= parseInt($("#Timeout").val());
+        config["Base"]["Archive"]= parseInt($("#Archive").val());
+        config["Base"]["Refresh"]= parseInt($("#Refresh").val());
+        config["Topology"]["Tsound"] = $("#Tsound").val();
+        config["Topology"]["Tline"] = $("#Tline").val();
+        config["Topology"]["Tsymbolsize"] = $("#Tsymbolsize").val();
+        config["Alert"]["EmailHost"] = $("#EmailHost").val();
+        config["Alert"]["SendEmailAccount"] = $("#SendEmailAccount").val();
+        config["Alert"]["SendEmailPassword"] = $("#SendEmailPassword").val();
+        config["Alert"]["RevcEmailList"] = $("#RevcEmailList").val();
+        config["Authiplist"] = $("#Authiplist").val();
+        config["Toollimit"] = parseInt($("#Toollimit").val());
+        data = {}
+        data['config'] = JSON.stringify(config)
+        data['password'] = $(".password").val()
+        $.ajax({
+            dataType: "json",
+            type: 'POST',
+            url: "/api/saveconfig.json",
+            data: data,
+            success: function( retdata ) {
+                if (retdata["status"]=="true"){
+                    alert("系统配置保存成功!")
+                    window.location.reload();
+                }else{
+                    alert("系统配置保存失败 ("+retdata["info"]+")")
+                }
+            },
+            timeout: 10*1000
+        }).fail( function( xhr, status ) {
+            alert("系统配置保存失败 (API错误!)")
+        });
+    }
+    //云模式保存配置
+    function savecloudconf(){
+        //config = {}
+        endpoint = $("#Endpoint").val();
+        $.getJSON("/api/proxy.json?g="+endpoint+"",function(config){
+            config["Mode"]["Endpoint"] = $("#Endpoint").val();
+            config["Mode"]["Type"] = "cloud";
+            //config["Mode"]["Status"] = "true";
+            config["Addr"] = $("#CloudSelfAddr").val();
+            config["Name"] = $("#CloudSelfName").val();
+            selfFlag = false
+            if(!config.hasOwnProperty("Network")){
+                alert("远程配置文件解析错误!")
+                return false
+            }
+            $.each(config["Network"], function(Addr, agentconfig){
+                //console.log("1 "+config["Addr"]+config["Name"])
+                //console.log("2 "+agentconfig["Addr"]+agentconfig["Name"]+Addr)
+                if(agentconfig["Addr"]==config["Addr"] && agentconfig["Name"]==config["Name"]&& Addr==config["Addr"] ){
+                    selfFlag=true
+                    return false
+                }
+            })
+            if(!selfFlag){
+                alert("配置文件错误,本机节点与Ping节点测试网络信息不匹配!");
+                return
+            }
+            data = {}
+            data['config'] = JSON.stringify(config)
+            data['password'] = $("#CloudPass").val()
+            $.ajax({
+                dataType: "json",
+                type: 'POST',
+                url: "/api/saveconfig.json",
+                data: data,
+                success: function( retdata ) {
+                    if (retdata["status"]=="true"){
+                        alert("系统配置保存成功!")
+                        window.location.reload();
+                    }else{
+                        alert("系统配置保存失败 ("+retdata["info"]+")")
+                    }
+                },
+                timeout: 10*1000
+            }).fail( function( xhr, status ) {
+                alert("系统配置保存失败 (API错误!)")
+            });
+        }).fail(function (xhr, status) {
+            //alert("123")
+            alert(xhr.responseText);
+        });
+    }
+    //选择是否为SmartPing
+    function checkSP(obj){
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if($(obj).is(":checked")){
+            $(obj).parent().parent().find("a").each(function(i,elm) {
+                $(elm).removeAttr("disabled")
+                config["Network"][Addr]["Smartping"]=true
+            })
+        }else{
+            $(obj).parent().parent().find("a").each(function(i,elm){
+              $(elm).attr("disabled","true")
+                config["Network"][Addr]["Smartping"]=false
+                config["Network"][Addr]["Ping"]=new Array()
+                config["Network"][Addr]["Tools"]=new Object()
+                config["Network"][Addr]["Tools"]["Icmpping"]=new Array()
+                config["Network"][Addr]["Topology"]=new Array()
+            })
+        }
+        sessionStorage.setItem("config",JSON.stringify(config))
+    }
+    $(function(){
+        $(".ttip").tooltip();
+        var config = new Object()
+        $.getJSON("/api/config.json",function(result){
+            AgentMode(result["Mode"])
+            if(result["Mode"]["Type"]=="cloud"){
+                $("#LastSuccTimeStr").html("<span class='alert-success' style='padding:10px;'>最近一次成功同步时间:"+result["Mode"]["LastSuccTime"]+"</span><br/>")
+            }else{
+                if( tp!="?dcloud" ) {
+                    $(".localmode").show();
+                    $(".cloudmode").remove();
+                }
+            }
+            $("#agentname").html(" ("+result["Name"]+")")
+            sessionStorage.setItem("config",JSON.stringify(result))
+            config=result
+            $("#verion").html("Version: "+config["Ver"])
+            $("#Timeout").val(config["Base"]["Timeout"]);
+            $("#Refresh").val(config["Base"]["Refresh"]);
+            $("#Archive").val(config["Base"]["Archive"]);
+            $("#Tsound").val(config["Topology"]["Tsound"]);
+            $("#Tline").val(config["Topology"]["Tline"]);
+            $("#Tsymbolsize").val(config["Topology"]["Tsymbolsize"]);
+            $("#EmailHost").val(config["Alert"]["EmailHost"]);
+            $("#SendEmailAccount").val(config["Alert"]["SendEmailAccount"]);
+            $("#SendEmailPassword").val(config["Alert"]["SendEmailPassword"]);
+            $("#RevcEmailList").val(config["Alert"]["RevcEmailList"]);
+            $("#Authiplist").val(config["Authiplist"]);
+            $("#Toollimit").val(config["Toollimit"]);
+            selfconfig=config["Network"][config["Addr"]]
+            $("#CloudSelfName").val(selfconfig["Name"])
+            $("#CloudSelfAddr").val(selfconfig["Addr"])
+            if(config["Mode"]["Endpoint"]!=""){
+                $("#Endpoint").val(config["Mode"]["Endpoint"]);
+            }
+            $(".selfagent").html("<tr>" +
+                "<td class='selfAgentName' id='selfAgentName'>"+selfconfig["Name"]+"</td><td class='selfAgentAddr Addr' id='selfAgentAddr'>"+selfconfig["Addr"]+"</td>" +
+                "<td style='text-align:center'><input type='checkbox' checked disabled></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='pingcfg(this,false)'>配置</a></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='topocfg(this,false)'>配置</a></td>" +
+                //"<td style='text-align:center'><a class='btn btn-mini' onclick='toolcfg(this,false)'>配置</a></td>" +
+                "<td style='text-align: center'><i onclick='editSelfagentModal(false);' class='icon-large icon-edit'></i>" +
+                "</tr>")
+            $.each(config["Network"], function(Addr, agentconfig){
+                if(config["Addr"]!=Addr){
+                    checked=""
+                    disabled="disabled"
+                    if(agentconfig["Smartping"]){
+                        checked="checked"
+                        disabled=""
+                    }
+                    $(".agentlist").append("<tr class='agentlist-"+agentconfig["Addr"]+"'>" +
+                        "<td>"+agentconfig["Name"]+"</td><td class='Addr'>"+agentconfig["Addr"]+"</td>" +
+                        "<td style='text-align:center'><input type='checkbox' "+checked+" onclick='checkSP(this)'></td>" +
+                        "<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='pingcfg(this,false)'>配置</a></td>" +
+                        "<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='topocfg(this,false)'>配置</a></td>" +
+                        //"<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='toolcfg(this,false)'>配置</a></td>" +
+                        "<td style='text-align: center'><i onclick='delAgent(this,null);' class='icon-large icon-trash'></i></td>" +
+                        "</tr>")
+                }
+            })
+            refreshChinaMap();
+        });
+        $(".allchecked").click(function(){
+            var xz = $(this).prop("checked");
+            var topoCheck=false
+            if($(this).attr("v")=="topoCheck"){
+                topoCheck=true
+            }
+            $(this).parents().children("tbody").children("tr").each(function(i,obj) {
+                $(obj).find('input').attr("checked",xz);
+                if(topoCheck){
+                    enabledTopo($(obj).find('input'))
+                }
+            });
+        });
+    });
+</script>
+
+</body>
+</html>

BIN
html/alert.mp3


+ 264 - 0
html/alerts.html

@@ -0,0 +1,264 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<link rel="stylesheet" type="text/css" href="assets/datatable/css/jquery.dataTables.min.css">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+      <!--/.nav-collapse -->
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li class="active"><a   href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li><a href="mapping.html" ><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container" id="main">
+      <div class="row">
+
+        <div class="span2"  >
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>报警存档</h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <thead>
+                <tr>
+                  <th> 日期 </th>
+                </tr>
+                </thead>
+                <tbody class="datelist"></tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+
+        </div>
+
+        <div class="span8">
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>报警历史</h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered" id="mytable">
+                <thead>
+                <tr>
+                  <th> 报警日期 </th>
+                  <th> 源节点 </th>
+                  <th> 源IP </th>
+                  <th> 目标节点 </th>
+                  <th> 目标IP </th>
+                  <th> 工具 </th>
+                </tr>
+                </thead>
+                <tbody class="alertlist"></tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+        </div>
+
+        <div class="span2">
+
+          <div style="text-align: center;margin-bottom: 15px;">
+            <button class="btn" style="width:100%;height:50px;margin-bottom: -5px;text-align: center" onclick="javascript:window.location.href='topology.html'"><i class="icon-circle-arrow-left "></i> 返回Ping拓扑</button>
+          </div>
+
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>节点列表</h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <tbody class="agentlist">
+
+                </tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+        </div>
+
+      </div>
+
+
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+
+<div id="alert" class="modal fade" tabindex="-1" style="width: 550px;height:500px;">
+  <!--<div class="modal-dialog">
+       <div class="modal-content"  >-->
+           <table class="table">
+             <thead><tr><td>Host</td><td>Loss%</td><td>Snt</td><td>Last</td><td>Avg</td><td>Best</td><td>Wrst</td><td>StDev</td></th></thead>
+             <tbody class="tracertdata"></tbody>
+           </table>
+  <!-- </div>
+  </div>/.modal-content -->
+  </div>.modal-dialog -->
+</div><!-- /.modal -->
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/jquery.base64.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/datatable/js/jquery.dataTables.min.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    function getdata(d){
+        var ajaxcnt = 0
+        $.getJSON("/api/config.json",function(result){
+            $("#agentname").html(" ("+result["Name"]+")")
+            AgentMode(result["Mode"])
+            $(".datelist").html("");
+            FromObj = new Object();
+            $.each(result["Network"], function(i, network) {
+                ArrObj = new Object()
+                ArrObj["Name"] = network["Name"]
+                ArrObj["Addr"] = network["Addr"]
+                ArrObj["Type"] = "To"
+                ArrObj["Color"] = "green"
+                if (network["Topology"].length > 0) {
+                    ArrObj["Color"] = "gray"
+                    ArrObj["Type"] = "From"
+                }
+                FromObj[network["Addr"]] = ArrObj
+            })
+            $(".agentlist").html("")
+            $.each(FromObj, function(i, network){
+                if(network["Type"]=="To"){
+                    return true
+                }
+                $(".agentlist").append("<tr><td><i class='icon-spinner icon-spin animated alerticon-" + network["Name"] + "' data-toggle='tooltip' title='' data-placement='bottom'></i>&nbsp;" + network["Name"] + "</td></tr>");
+            });
+            $(".alertlist").html("");
+            $.each(FromObj, function(i, network){
+                    if(network["Type"]=="To"){
+                        return true
+                    }
+                    if (d==""){
+                        rurl = '/api/proxy.json?g=http://' +network["Addr"]+':'+result['Port']+'/api/alert.json'
+                    }else{
+                        rurl = '/api/proxy.json?g=http://' +network["Addr"]+':'+result['Port']+'/api/alert.json?date='+d
+                    }
+                    ajaxcnt = ajaxcnt+1
+                    $.ajax({
+                        dataType: "json",
+                        url: rurl,
+                        success: function( result) {
+                            ajaxcnt = ajaxcnt - 1
+                            $(".alerticon-"+network["Name"]+"").remove();
+                            for (var del in result[0]) {
+                                var ard = false
+                                $(".datelist").find("tr").each(function () {
+                                    if ($(this).find("td").find("a").html() == result[0][del]) {
+                                        ard = true
+                                    }
+                                });
+                                if (ard == false) {
+                                    $(".datelist").append("<tr><td><a onclick=getdata(\"" + result[0][del] + "\")>" + result[0][del] + "</a></td></tr>");
+                                }
+                            };
+                            for(var dal in result[1]){
+                                $(".alertlist").append("<tr><td>"+result[1][dal]['Logtime']+"</td><td>"+result[1][dal]['Fromname']+"</td><td>"+result[1][dal]['Fromip']+"</td><td>"+result[1][dal]['Targetname']+"</td><td>"+result[1][dal]['Targetip']+"</td><td><a onclick=\"gettracertdata(this)\" value='"+result[1][dal]['Tracert']+"'>MTR</a></td></tr>");
+                            };
+                            if(ajaxcnt==0){
+                                pageFinish()
+                            }
+                        },
+                        timeout: result["Base"]["Timeout"]*1000
+                    }).fail( function( xhr, status ) {
+                        ajaxcnt = ajaxcnt -1;
+                        $(".alerticon-"+network["Name"]+"").removeClass("icon-spinner").removeClass("icon-spin").removeClass("animated");
+                        $(".alerticon-"+network["Name"]+"").addClass("icon-warning-sign");
+                        $(".alerticon-" + network["Name"] + "").attr("title",xhr.responseText);
+                        $(".alerticon-" + network["Name"] + "").tooltip();
+                        if(ajaxcnt==0){
+                            pageFinish()
+                        }
+                    });
+            });
+
+        });
+    }
+    function gettracertdata(d){
+        content = $.parseJSON($(d).attr("value"))
+        $(".tracertdata").html("")
+        $.each(content,function(i,line){
+            $(".tracertdata").append("<tr><td>"+line["Host"]+"</td><td>"+((line["Loss"]/line["Send"])*100).toFixed(2)+"</td><td>"+line["Send"]+"</td><td>"+(line["Last"]/1000000).toFixed(2)+"</td><td>"+(line["Avg"]/1000000).toFixed(2)+"</td><td>"+(line["Best"]/1000000).toFixed(2)+"</td><td>"+(line["Wrst"]/1000000).toFixed(2)+"</td><td>"+line["StDev"].toFixed(2)+"</td></tr>")
+            console.log(line["Loss"])
+        })
+        $("#alert").modal('show');
+    }
+    function pageFinish(){
+        $('#mytable').DataTable({
+            "paging": false,
+            "aaSorting": [[ 0, "desc" ]]
+        });
+    }
+    $(function(){
+        getdata("")
+
+    });
+</script>
+</body>
+</html>

+ 1216 - 0
html/config.html

@@ -0,0 +1,1216 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+    <div class="navbar-inner">
+        <div class="container">
+          <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+          <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+            <div class="nav-collapse">
+            <!--
+            <ul class="nav pull-right">
+                <li class="dropdown">
+                    <a target="_blank" href="http://smartping.cn/" >
+                        <i class="icon-comment"></i>
+                        Know About SmartPing.CN ?
+                    </a>
+                </li>
+            </ul>
+            -->
+        </div>
+            <!--/.nav-collapse -->
+        </div>
+        <!-- /container -->
+    </div>
+    <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li ><a   href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+          <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+          <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+          <li class="active"><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main localmode" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container" id="main">
+      <div class="row">
+          <div class="span4">
+            <div class="widget-header">
+              <i class="icon-user"></i>
+              <h3>保存配置 </h3>
+               <span style="float: right;margin-right: 25px;"><a  target="_blank" href="/api/config.json"><i class="icon-file"></i></a></span>
+            </div> <!-- /widget-header -->
+            <div class="widget-content">
+              <div style="text-align: center">
+                <span>
+                  <b>密码:</b>
+                  <input style="width:120px;" class="password" type="password" id="pass">
+                  <button style="margin: -10px 0 0 0;" type="submit" class="btn btn-primary" onclick="saveconf()">保存</button>
+                </span>
+                <div style="text-align: center"><a href="?dcloud"><i class="icon-cloud"></i> 切换到云模式 </a></div>
+              </div>
+
+            </div>
+            <br/>
+            <div class="widget-header">
+              <i class="icon-user"></i>
+              <h3>基础配置</h3>
+            </div> <!-- /widget-header -->
+
+            <div class="widget-content">
+
+            <h4>基础</h4><br>
+
+              <div class="control-group"  style="float: left;width: 28%;">
+                <label class="control-label" >接口超时(秒)</label>
+                <div class="controls">
+                  <input type="text" style="width:100%" id="Timeout" value="" >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+              <div class="control-group"  style="float: left;width: 28%;margin-left: 20px;">
+                <label class="control-label" for="Refresh">页面刷新(分钟)</label>
+                <div class="controls">
+                  <input type="text"   style="width:100%" id="Refresh" value=""  >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+
+              <div class="control-group"  style="width: 28%;float: left;margin-left: 20px;">
+                <label class="control-label ttip" for="Archive" data-toggle='tooltip' title='此配置仅针对全国延迟与报警日志数据生效,Ping数据默认保存30天不可修改!'  data-placement='bottom'>数据存档(天)</label>
+                <div class="controls">
+                  <input type="text" style="width:100%"   id="Archive" value=""  >
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+            <hr style="clear:both;">
+            <h4>Ping拓扑</h4><br>
+
+            <div class="control-group" style="float: left;width: 43%;">
+              <label class="control-label" for="Tsound">报警声音</label>
+              <div class="controls">
+                <input type="text"  style="width:97%" id="Tsound" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+            <div class="control-group"  style="width: 20%;float: left;margin-left: 20px;">
+              <label class="control-label" for="Tline">连线粗细</label>
+              <div class="controls">
+                <input type="text"  style="width:100%"  id="Tline" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+            <div class="control-group"  style="width: 20%;float: left;margin-left: 20px;">
+              <label class="control-label" for="Tsymbolsize">形状大小</label>
+              <div class="controls">
+                <input type="text" style="width:100%"   id="Tsymbolsize" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+           <hr style="clear:both;">
+           <h4>报警邮件<button style="float: right" onclick="sendMailTest(this)">发送测试邮件</button></h4><br>
+            <div class="control-group" >
+                <label class="control-label" for="Tsymbolsize">邮件服务器</label>
+                <div class="controls">
+                    <input type="text" style="width:97%"   id="EmailHost" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group"  style="width: 46%;float: left;">
+                <label class="control-label" for="Tsymbolsize">发件邮件</label>
+                <div class="controls">
+                    <input type="text" style="width:100%"   id="SendEmailAccount" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group"  style="width: 46%;float: left;margin-left: 20px;">
+                <label class="control-label" for="Tsymbolsize">发件邮箱密码</label>
+                <div class="controls">
+                    <input type="password" style="width:100%"   id="SendEmailPassword" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="control-group" >
+                <label class="control-label" for="Tsymbolsize">收件邮箱列表(分号隔开)</label>
+                <div class="controls">
+                    <input type="text" style="width:97%"   id="RevcEmailList" value="">
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+
+
+              <hr style="clear:both;">
+
+              <h4>检测工具</h4><br>
+
+              <div class="control-group"  style="float: left;width: 45%;">
+                <label class="control-label" for="Toollimit">限定频率(秒)</label>
+                <div class="controls">
+                  <input type="text"   style="width:100%" id="Toollimit" value="">
+                </div> <!-- /controls -->
+              </div> <!-- /control-group -->
+
+            <hr style="clear:both;">
+            <h4>授权管理</h4><br>
+            <div class="control-group"  style="width: 96%;">
+                <label class="control-label" for="Authiplist">用户IP列表(逗号隔开)</label>
+                <div class="controls">
+                    <input type="text" style="width:100%"  id="Authiplist" value="" >
+                </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <br/>
+
+          </div>
+          </div>
+          <div class="span8">
+            <!-- /widget -->
+            <div class="widget widget-table action-table">
+                <div class="widget-header"> <i class="icon-th-list"></i>
+                  <h3>Ping节点测试网络</h3>
+                </div>
+                <!-- /widget-header -->
+                <div class="widget-content">
+                  <table class="table table-striped table-bordered">
+                    <thead>
+                      <tr>
+                        <th> 节点名称(本机) </th>
+                        <th> 节点IP(本机) </th>
+                        <th> SmartPing </th>
+                        <th style="width:60px;"> 正向Ping </th>
+                        <th style="width:60px;text-align: center">Ping拓扑</th>
+                        <!--<th style="width:60px;text-align: center">检测工具</th>-->
+                        <th  style="width:15px;">  </th>
+                      </tr>
+                    </thead>
+                    <tbody  class="selfagent"></tbody>
+                    <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:60px;"> SmartPing </th>
+                        <th style="width:60px;"> 正向Ping </th>
+                        <th style="width:60px;text-align: center">Ping拓扑</th>
+                        <!--<th style="width:60px;text-align: center">检测工具</th>-->
+                        <th  style="width:15px;">  </th>
+                      </tr>
+                    </thead>
+                    <tbody class="agentlist"></tbody>
+                    <tfoot>
+                      <tr>
+                        <td colspan="8" style="text-align: center"><input style='margin:4px 3px 0 0;' type='button' class='btn btn-small' value='添加节点' data-toggle="modal" data-target="#agentModal"></td>
+                      </tr>
+                    </tfoot>
+                  </table>
+                </div>
+                <!-- /widget-content -->
+              </div>
+
+            <!-- editSelfAgentModal -->
+            <div class="modal fade" id="editSelfagentModal" tabindex="-1" role="dialog"  style="width:480px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">编辑本机节点</h4>
+                  </div>
+                  <div class="modal-body form-horizontal" >
+                    节点名称:<input id="editAgentName" type="text" style="width: 150px;"> 节点IP: <input id="editAgentIp" type="text" style="width: 150px;">
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="editSelfagentModal(true)">暂存</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- NewAgentModal -->
+            <div class="modal fade" id="agentModal" tabindex="-1" role="dialog"  style="width:480px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">新增节点</h4>
+                  </div>
+                  <div class="modal-body form-horizontal" >
+                    节点名称:<input id="newAgentName" type="text" style="width: 150px;"> 节点IP: <input id="newAgentIp" type="text" style="width: 150px;">
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="addAgent(null,null)">暂存</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- PingModal -->
+            <div class="modal fade" id="pingModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:500px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">Ping配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="text-align: center"> 正向Ping <input type='checkbox' class="allchecked"></th>
+                      </tr>
+                      </thead>
+                      <tbody class="pingModalist"></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="pingcfg(null,true)" >暂存</button>
+                    <input type='hidden' id='pingcfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- TopoModal -->
+            <div class="modal fade" id="topoModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:800px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="myModalLabel">拓扑配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:70px;"> 正向拓扑 <input type='checkbox' class="allchecked" style="margin-top: 2px"  v="topoCheck" /></th>
+                        <th style="width:450px;"> 报警规则 </th>
+                      </tr>
+                      </thead>
+                      <tbody class="topoModallist"></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="topocfg(null,true)">暂存</button>
+                    <input type='hidden' id='topocfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- ToolsModal
+            <div class="modal fade" id="toolModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:800px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title">工具配置</h4>
+                  </div>
+                  <div class="modal-body">
+                    <table class="table table-striped table-bordered">
+                      <thead>
+                      <tr>
+                        <th> 节点名称 </th>
+                        <th> 节点IP </th>
+                        <th style="width:450px;"> 可用工具 </th>
+                      </tr>
+                      </thead>
+                      <tbody class="toolModallist" ></tbody>
+                    </table>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" onclick="toolcfg(null,true)">暂存</button>
+                    <input type='hidden' id='toolscfgAddr' value=''/>
+                  </div>
+                </div>
+              </div>
+            </div>
+            -->
+
+            <!-- /全国延迟测试网络 -->
+            <div class="widget widget-table action-table">
+              <div class="widget-header"> <i class="icon-th-list"></i>
+                <h3>全国延迟测试网络</h3>
+              </div>
+              <!-- /widget-header -->
+              <div class="widget-content chinamaplist" style="padding:10px;"></div>
+            <!-- /widget -->
+            </div>
+
+            <!-- chinamapModal -->
+            <div class="modal fade" id="addChinaMapModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:550px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" >新增全国延迟测试节点</h4>
+                  </div>
+                  <div class="modal-body ">
+                    <div style="text-align: center"><select class='provSelect' style='width:70px;'><option value='北京'>北京</option><option value='浙江'>浙江</option><option value='天津'>天津</option><option value='安徽'>安徽</option><option value='上海'>上海</option><option value='福建'>福建</option><option value='重庆'>重庆</option><option value='江西'>江西</option><option value='山东'>山东</option><option value='河南'>河南</option><option value='湖北'>湖北</option><option value='湖南'>湖南</option><option value='广东'>广东</option><option value='海南'>海南</option><option value='山西'>山西</option><option value='青海'>青海</option><option value='江苏'>江苏</option><option value='辽宁'>辽宁</option><option value='吉林'>吉林</option><option value='台湾'>台湾</option><option value='河北'>河北</option><option value='贵州'>贵州</option><option value='四川'>四川</option><option value='云南'>云南</option><option value='陕西'>陕西</option><option value='甘肃'>甘肃</option><option value='黑龙江'>黑龙江</option><option value='香港'>香港</option><option value='澳门'>澳门</option><option value='广西'>广西</option><option value='宁夏'>宁夏</option><option value='新疆'>新疆</option><option value='内蒙古'>内蒙古</option><option value='西藏'>西藏</option></select></div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 电信 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_ctcc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_ctcc')">
+                    </div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 联通 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_cucc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_cucc')">
+                    </div>
+                    <div style="float: left;width: 31%;padding:5px;">
+                      <table class="table table-striped table-bordered">
+                        <thead>
+                        <tr>
+                          <th> 移动 </th>
+                        </tr>
+                        </thead>
+                        <tbody class="add_chinamap_cmcc" ></tbody>
+                      </table>
+                      <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('add_chinamap_cmcc')">
+                    </div>
+                  </div>
+                </div>
+                <div class="modal-footer">
+                  <button type="button" class="btn btn-primary" onclick="addChinaMap(true)">暂存</button>
+                </div>
+              </div>
+            </div>
+
+
+            <!-- chinamapModal -->
+            <div class="modal fade" id="chinaMapModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="width:550px;">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title editChinaMapProv" ></h4>
+                  </div>
+                  <div class="modal-body ">
+                    <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                            <tr>
+                              <th> 电信 </th>
+                            </tr>
+                          </thead>
+                          <tbody class="chinamap_ctcc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_ctcc')">
+                      </div>
+                      <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                          <tr>
+                            <th> 联通 </th>
+                          </tr>
+                          </thead>
+                          <tbody class="chinamap_cucc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_cucc')">
+                      </div>
+                      <div style="float: left;width: 31%;padding:5px;">
+                        <table class="table table-striped table-bordered form-horizontal">
+                          <thead>
+                          <tr>
+                            <th> 移动 </th>
+                          </tr>
+                          </thead>
+                          <tbody class="chinamap_cmcc" ></tbody>
+                        </table>
+                        <input type="button" style="width: 100%;margin-top: -10px;" value=" + " onclick="addChinaMapCell('chinamap_cmcc')">
+                      </div>
+                  </div>
+                  </div>
+                  <div class="modal-footer">
+                    <button type="button" class="btn btn-danger pull-left" onclick="delChinaMap()">删除</button>
+                    <button type="button" class="btn btn-primary" onclick="editChinaMap(null,true)">暂存</button>
+                  </div>
+                </div>
+              </div>
+
+
+          </div>
+          </div>
+      <hr>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+<div class="main cloudmode" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span3"> &nbsp;</div>
+        <div class="span6">
+            <div class="widget-content">
+                <div id="LastSuccTimeStr" style="text-align: center"></div>
+                <div class="control-group">
+              <label class="control-label">云配置地址</label>
+              <div class="controls">
+                <div class="input-append">
+                  <input  style="width:98%" class="m-wrap" id="Endpoint" value="http://" type="text">
+                </div>
+              </div>	<!-- /controls -->
+            </div> <!-- /control-group -->
+            <div class="row">
+              <div class="span2" style="width: 44.4%;">
+                <div class="control-group">
+                  <label class="control-label">节点名称(本机)</label>
+                  <div class="controls">
+                    <input class="cpassword" type="text" style="width:97%"  id="CloudSelfName" value="">
+                  </div> <!-- /controls -->
+                </div> <!-- /control-group -->
+              </div>
+              <div class="span2" style="width: 44.4%;">
+                <div class="control-group">
+                  <label class="control-label">节点IP(本机)</label>
+                  <div class="controls">
+                    <input class="cpassword" type="text" style="width:97%"  id="CloudSelfAddr" value="">
+                  </div> <!-- /controls -->
+                </div> <!-- /control-group -->
+              </div>
+            </div>
+            <div class="control-group">
+              <label class="control-label">密码</label>
+              <div class="controls">
+                <input class="cpassword" type="password" style="width:97%"  id="CloudPass" value="">
+              </div> <!-- /controls -->
+            </div> <!-- /control-group -->
+            <button style="width:100%"  type="submit" class="btn btn-primary" onclick="savecloudconf()">检测并保存</button>
+            <div style="text-align: center;margin-top: 10px;"><a  href="?local"><i class="icon-home"></i> 切换到本地模式 </a></div>
+          </div>
+        </div>
+        <div class="span3">&nbsp;</div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/jquery.cookie.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    var tp = window.location.search;
+    //console.log(tp)
+    if(tp=="?cloud" || tp=="?dcloud" ){
+        $(".localmode").hide();
+        $(".cloudmode").show();
+    }
+    if(tp=="?local"){
+        $(".localmode").show();
+        $(".cloudmode").hide();
+    }
+    //Ping节点测试网络 - 编辑本机节点
+    function editSelfagentModal(Save){
+        if(Save){
+            editAgentName = $("#editAgentName").val();
+            editAgentAddr = $("#editAgentIp").val();
+            config=$.parseJSON(sessionStorage.getItem("config"))
+            config["Name"]=editAgentName
+            config["Addr"]=editAgentAddr
+            sessionStorage.setItem("config",JSON.stringify(config))
+            delAgent(null,$(".selfAgentAddr").html())
+            $(".selfAgentName").html(editAgentName)
+            $(".selfAgentAddr").html(editAgentAddr)
+            addAgent(editAgentName,editAgentAddr)
+            config["Network"][editAgentAddr]["Smartping"]=true
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#editSelfagentModal').modal("hide");
+            return
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        $("#editAgentName").val(config["Name"]);
+        $("#editAgentIp").val(config["Addr"]);
+        $('#editSelfagentModal').modal();
+    }
+    //Ping节点测试网络 - 新增节点
+    function addAgent(Name,Ip){
+        if(Name==null && Ip==null){
+            newAgentName = $("#newAgentName").val();
+            newAgentIp = $("#newAgentIp").val();
+        }else{
+            newAgentName = Name
+            newAgentIp = Ip
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        //检测节点IP或Name是否重复
+        CheckFlag=""
+        $.each(config["Network"],function(Addr,network){
+            if(Addr==newAgentIp || network["Addr"]==newAgentIp){
+                CheckFlag = "节点IP不能重复!"
+                return false
+            }
+            if( network["Name"]==newAgentName){
+                CheckFlag = "节点名称不能重复!"
+                return false
+            }
+        })
+        if(CheckFlag!=""){
+            alert(CheckFlag)
+            return
+        }
+        //添加新节点
+        if(Name==null && Ip==null){
+            $(".agentlist").append("<tr class='agentlist-"+newAgentIp+"'>" +
+                "<td>"+newAgentName+"</td><td class='Addr'>"+newAgentIp+"</td>" +
+                "<td style='text-align:center'><input type='checkbox' onclick='checkSP(this)'></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='pingcfg(this,null)' disabled='disabled'>配置</a></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='topocfg(this,false)' disabled='disabled'>配置</a></td>" +
+                //"<td style='text-align:center'><a class='btn btn-mini' onclick='toolcfg(this,null)' disabled='disabled'>配置</a></td>" +
+                "<td><i onclick='delAgent(this,null);' class='icon-large icon-trash'></i></td>" +
+                "</tr>")
+        }
+        agent=new Object()
+        agent["Name"] = newAgentName
+        agent["Addr"]=newAgentIp
+        agent["Ping"]=new Array()
+        //agent["Tools"]=new Object()
+        //agent["Tools"]["Icmpping"]=new Array()
+        agent["Topology"]=new Array()
+        /*
+        $.each(config["Network"], function(Addr, agentconfig){
+            agent["Ping"].push(Addr)
+            agent["Tools"]["Icmpping"].push(Addr)
+            topoMap = new Map()
+            topoMap["Name"]=agentconfig["Name"]
+            topoMap["Addr"]=agentconfig["Addr"]
+            topoMap["Thdchecksec"]="0"
+            topoMap["Thdoccnum"]="0"
+            topoMap["Thdavgdelay"]="0"
+            topoMap["Thdloss"]="0"
+            agent["Topology"].push(topoMap)
+        })
+        */
+        config["Network"][newAgentIp]=agent
+       //每个已有节点下加入新节点
+        /*
+        $.each(config["Network"], function(Addr, obj){
+            config["Network"][Addr]["Ping"].push(newAgentIp);
+            config["Network"][Addr]["Tools"]["Icmpping"].push(newAgentIp);
+            NewtopoMap = new Map()
+            NewtopoMap["Name"]=newAgentName
+            NewtopoMap["Addr"]=newAgentIp
+            NewtopoMap["Thdchecksec"]="0"
+            NewtopoMap["Thdoccnum"]="0"
+            NewtopoMap["Thdavgdelay"]="0"
+            NewtopoMap["Thdloss"]="0"
+            config["Network"][Addr]["Topology"].push(NewtopoMap)
+        })
+        */
+        config["Network"][config["Addr"]]["Ping"].push(newAgentIp);
+        //config["Network"][config["Addr"]]["Tools"]["Icmpping"].push(newAgentIp);
+        NewtopoMap = new Map()
+        NewtopoMap["Name"]=newAgentName
+        NewtopoMap["Addr"]=newAgentIp
+        NewtopoMap["Thdchecksec"]="900"
+        NewtopoMap["Thdoccnum"]="3"
+        NewtopoMap["Thdavgdelay"]="200"
+        NewtopoMap["Thdloss"]="30"
+        config["Network"][config["Addr"]]["Topology"].push(NewtopoMap)
+        sessionStorage.setItem("config",JSON.stringify(config))
+        $("#newAgentName").val("")
+        $("#newAgentIp").val("")
+        $('#agentModal').modal('hide');
+    }
+    //Ping节点测试网络 - 删除节点
+    function delAgent(obj,SelfAddr){
+        if(SelfAddr!=null){
+            Addr=SelfAddr
+        }else{
+            Addr=$(obj).parent().parent().find(".Addr").html()
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        delete config["Network"][Addr];
+        $.each(config["Network"], function(iAddr, agentconfig){
+            $.each(agentconfig["Ping"], function(pingIndex, pingAddr){
+                if(pingAddr==Addr){
+                    config["Network"][iAddr]["Ping"].splice(pingIndex,1)
+                    return false
+                }
+            })
+            /*
+            $.each(agentconfig["Tools"]["Icmpping"], function(toolIcmpIndex, toolIcmpAddr){
+                if(toolIcmpAddr==Addr){
+                    config["Network"][iAddr]["Tools"]["Icmpping"].splice(toolIcmpIndex,1)
+                    return false
+                }
+            })
+            */
+            $.each(agentconfig["Topology"], function(topoIndex, topoObj){
+                if(topoObj["Addr"]==Addr){
+                    config["Network"][iAddr]["Topology"].splice(topoIndex,1)
+                    return false
+                }
+            })
+        })
+        sessionStorage.setItem("config",JSON.stringify(config))
+        $(obj).parent().parent().remove();
+    }
+    //Ping节点测试网络 - 正向PING配置弹窗
+    function pingcfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        $(".allchecked").removeAttr("checked")
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save==true){
+            nAddr=$("#pingcfgAddr").val()
+            NewPingList = new Array()
+            $('.pingModalist tr').each(function(i){
+                if($(this).children('td').children('input').is(":checked")){
+                    NewPingList.push($(this).find(".pingModalistAddr").html())
+                }
+            });
+            config["Network"][nAddr]["Ping"]=NewPingList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#pingModal').modal("hide")
+            return
+        }
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        selfconfig=config["Network"][Addr];
+        $(".pingModalist").html("")
+        $.each(config["Network"], function(iAddr, agentconfig){
+            checked=""
+            $.each(selfconfig["Ping"], function(i, ip){
+                if(agentconfig["Addr"]==ip){
+                    checked="checked"
+                    return false
+                }
+            })
+            $(".pingModalist").append("<tr>" +
+                "<td>"+agentconfig["Name"]+"</td>" +
+                "<td class='pingModalistAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  class='form-horizontal' style='text-align: center'><input type='checkbox' style='margin-top: -3px' "+checked+"></td>" +
+             "</tr>")
+        })
+        $("#pingcfgAddr").val(Addr)
+        $('#pingModal').modal()
+    }
+    //Ping节点测试网络 - 拓扑配置弹窗
+    function topocfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        $(".allchecked").removeAttr("checked")
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save==true){
+            mnAddr=$("#topocfgAddr").val()
+            NewTopoList = new Array()
+            $('.topoModallist tr').each(function(i){
+                //console.log($(this).find(".topoModalListChecked").is(":checked"))
+                if($(this).find(".topoModalListChecked").is(":checked")){
+                    topoModal = new Map()
+                    topoModal["Name"] = $(this).find(".topoModalListName").html()
+                    topoModal["Addr"] = $(this).find(".topoModalListAddr").html()
+                    topoModal["Thdchecksec"] = $(this).find(".topoModalListThdchecksec").val()
+                    topoModal["Thdoccnum"] = $(this).find(".topoModalListThdoccnum").val()
+                    topoModal["Thdavgdelay"] = $(this).find(".topoModalListThdavgdelay").val()
+                    topoModal["Thdloss"] = $(this).find(".topoModalListThdloss").val()
+                    NewTopoList.push(topoModal)
+                }
+            })
+            config["Network"][mnAddr]["Topology"]=NewTopoList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#topoModal').modal("hide")
+            return
+        }
+        mAddr=$(obj).parent().parent().find(".Addr").html()
+        $(".topoModallist").html("")
+        $.each(config["Network"], function(Addr, agentconfig){
+            var itopocfg= new Map()
+            itopocfg["Thdchecksec"] = 0
+            itopocfg["Thdoccnum"] = 0
+            itopocfg["Thdavgdelay"] = 0
+            itopocfg["Thdloss"] = 0
+            checked=""
+            disabled="disabled"
+            if(config["Network"].hasOwnProperty(mAddr)){
+                nodeConfig=config["Network"][mAddr]
+                $.each(nodeConfig["Topology"], function(i, cf){
+                    if(cf["Addr"]==Addr){
+                        itopocfg = cf
+                        checked="checked"
+                        disabled=""
+                        return false
+                    }
+                });
+            }
+            $(".topoModallist").append("<tr>" +
+                "<td class='topoModalListName'>"+agentconfig["Name"]+"</td>" +
+                "<td class='topoModalListAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  style='text-align:center'><input class='topoModalListChecked' type='checkbox' "+checked+" onclick='enabledTopo(this)' ></td>" +
+                "<td class='form-horizontal'>" +
+                "<div class='controls' style='margin-left:0px;'>" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdchecksec' type='text' style='width:25px' value='"+itopocfg["Thdchecksec"]+"' "+disabled+" >" +
+                "<span class='add-on'>秒</span>" +
+                "</div>" +
+                "内出现" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdoccnum' type='text' style='width:20px' value='"+itopocfg["Thdoccnum"]+"' "+disabled+" >" +
+                "<span class='add-on'>次</span>" +
+                "</div>" +
+                "内延迟大于" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdavgdelay' type='text' value='"+itopocfg["Thdavgdelay"]+"' style='width:25px' "+disabled+" >" +
+                "<span class='add-on'>ms</span>" +
+                "</div>" +
+                "或丢包率大于" +
+                "<div class='input-prepend input-append'>" +
+                "<input class='topoModalListThdloss' type='text'  value='"+itopocfg["Thdloss"]+"' style='width:20px' "+disabled+" >" +
+                "<span class='add-on'>%</span>" +
+                "</div>" +
+                "</div>" +
+                "</td>" +
+                "</tr>")
+        })
+        $("#topocfgAddr").val(mAddr)
+        $('#topoModal').modal()
+    }
+    function enabledTopo(obj){
+        if($(obj).is(":checked")){
+            $(obj).closest("tr").find(".topoModalListThdchecksec").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdchecksec").val("900")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").val("3")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").val("200")
+            $(obj).closest("tr").find(".topoModalListThdloss").removeAttr("disabled")
+            $(obj).closest("tr").find(".topoModalListThdloss").val("30")
+        }else{
+            $(obj).closest("tr").find(".topoModalListThdchecksec").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdchecksec").val("0")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdoccnum").val("0")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdavgdelay").val("0")
+            $(obj).closest("tr").find(".topoModalListThdloss").attr("disabled","true")
+            $(obj).closest("tr").find(".topoModalListThdloss").val("0")
+        }
+    }
+    /*/Ping节点测试网络 - 检测工具配置
+    function toolcfg(obj,Save){
+        if($(obj).attr("disabled") == "disabled"){
+            return
+        }
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save){
+            nAddr=$("#toolscfgAddr").val()
+            NewToolsIcmpList = new Array()
+            $('.toolModallist tr').each(function(i){
+                if($(this).children('td').children('input').is(":checked")){
+                    NewToolsIcmpList.push($(this).find(".toolModalistAddr").html())
+                }
+            });
+            config["Network"][nAddr]["Tools"]["Icmpping"]=NewToolsIcmpList;
+            sessionStorage.setItem("config",JSON.stringify(config))
+            $('#toolModal').modal("hide")
+            return
+        }
+        $(".toolModallist").html("")
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        selfconfig=config["Network"][Addr]
+        $.each(config["Network"], function(iAddr, agentconfig){
+            checked=""
+            $.each(selfconfig["Tools"]["Icmpping"], function(i, ip){
+                if(agentconfig["Addr"]==ip){
+                    checked="checked"
+                    return false
+                }
+            })
+            $(".toolModallist").append("<tr>" +
+                "<td>"+agentconfig["Name"]+"</td>" +
+                "<td class='toolModalistAddr'>"+agentconfig["Addr"]+"</td>" +
+                "<td  class='form-horizontal' style='text-align: left'><input type='checkbox' style='margin-top: -3px' "+checked+">ICMP PING</td>" +
+                "</tr>")
+        })
+        $('#toolscfgAddr').val(Addr)
+        $('#toolModal').modal()
+    }
+    */
+    //发送邮件测试
+    function sendMailTest(obj){
+        $(obj).html("测试邮件发送中(最长1分钟,请等待)...")
+        data = {}
+        data["EmailHost"] = $("#EmailHost").val();
+        data["SendEmailAccount"] = $("#SendEmailAccount").val();
+        data["SendEmailPassword"] = $("#SendEmailPassword").val();
+        data["RevcEmailList"] = $("#RevcEmailList").val();
+        $.ajax({
+            dataType: "json",
+            type: 'POST',
+            url: "/api/sendmailtest.json",
+            data: data,
+            success: function( retdata ) {
+                if (retdata["status"]=="true"){
+                    alert("测试邮件发送成功!")
+                }else{
+                    alert("测试邮件发送失败 ("+retdata["info"]+")")
+                }
+                $(obj).html("发送测试邮件")
+            },
+            timeout: 65*1000
+        }).fail( function( xhr, status ) {
+            alert("测试邮件发送失败 (API错误!)")
+            $(obj).html("发送测试邮件")
+        });
+    }
+    //全国延迟测试网络-新增节点
+    function addChinaMap(Save){
+        if(Save){
+            nprov=$('.provSelect').attr('value')
+            cmap=new Map()
+            cucc=new Array()
+            $(".add_chinamap_cucc").children('tr').children('td').children('.val').each(function(i,ip){
+                cucc.push($(ip).val())
+            })
+            cmap["cucc"]=cucc
+            ctcc=new Array()
+            $(".add_chinamap_ctcc").children('tr').children('td').children('.val').each(function(i,ip){
+                ctcc.push($(ip).val())
+            })
+            cmap["ctcc"]=ctcc
+            cmcc=new Array()
+            $(".add_chinamap_cmcc").children('tr').children('td').children('.val').each(function(i,ip){
+                cmcc.push($(ip).val())
+            })
+            cmap["cmcc"]=cmcc
+            config["Chinamap"][nprov]=cmap
+            $('#addChinaMapModal').modal("hide")
+            sessionStorage.setItem("config",JSON.stringify(config))
+            refreshChinaMap()
+            return
+        }
+        $.each(config["Chinamap"], function(prov, detail){
+            $(".provSelect option[value='"+prov+"']").remove();
+        })
+        $(".add_chinamap_cucc").find("tr").remove()
+        $(".add_chinamap_ctcc").find("tr").remove()
+        $(".add_chinamap_cmcc").find("tr").remove()
+        $('#addChinaMapModal').modal()
+    }
+    //全国延迟测试网络-删除节点
+    function delChinaMap(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        nprov=$(".editChinaMapProv").html()
+        delete config["Chinamap"][nprov]
+        $('#chinaMapModal').modal("hide")
+        sessionStorage.setItem("config",JSON.stringify(config))
+        refreshChinaMap()
+        return
+    }
+    //全国延迟测试网络-编辑节点
+    function editChinaMap(prov,Save){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if(Save){
+            nprov=$(".editChinaMapProv").html()
+            cmap=new Map()
+            cucc=new Array()
+            $(".chinamap_cucc").children('tr').children('td').children('.val').each(function(i,ip){
+                cucc.push($(ip).val())
+            })
+            cmap["cucc"]=cucc
+            ctcc=new Array()
+            $(".chinamap_ctcc").children('tr').children('td').children('.val').each(function(i,ip){
+                ctcc.push($(ip).val())
+            })
+            cmap["ctcc"]=ctcc
+            cmcc=new Array()
+            $(".chinamap_cmcc").children('tr').children('td').children('.val').each(function(i,ip){
+                cmcc.push($(ip).val())
+            })
+            cmap["cmcc"]=cmcc
+            config["Chinamap"][nprov]=cmap
+            $('#chinaMapModal').modal("hide")
+            sessionStorage.setItem("config",JSON.stringify(config))
+            refreshChinaMap()
+            return
+        }
+        $(".editChinaMapProv").html(prov)
+        $(".chinamap_cmcc").html("")
+        $(".chinamap_ctcc").html("")
+        $(".chinamap_cucc").html("")
+        detail = config["Chinamap"][prov]
+        $.each(detail["cmcc"],function(i,cmcc){
+            $(".chinamap_cmcc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+cmcc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $.each(detail["ctcc"],function(i,ctcc){
+            $(".chinamap_ctcc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+ctcc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $.each(detail["cucc"],function(i,cucc){
+            $(".chinamap_cucc").append("<tr><td><input class='val' type='text' style='width: 108px' value='"+cucc+"'> <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+        })
+        $('#chinaMapModal').modal()
+    }
+    //全国延迟测试网络-刷新面板
+    function refreshChinaMap(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        $(".chinamaplist").html("")
+        $.each(config["Chinamap"], function(prov, detail){
+            cmcc=detail["cmcc"].length
+            ctcc=detail["ctcc"].length
+            cucc=detail["cucc"].length
+            text=prov+"(电信"+ctcc+",联通"+cucc+",移动"+cmcc+")"
+            $(".chinamaplist").append("<input type='button' value='"+text+"' onclick='editChinaMap(\""+prov+"\")' style='margin-right: 10px;width:177px;'>")
+        })
+        $(".chinamaplist").append("<input type='button' value=' + ' style='width: 50px;' onclick='addChinaMap(false)'> ")
+    }
+    //全国延迟测试网络-添加IP条目
+    function addChinaMapCell(cell){
+        $("."+cell).append("<tr><td><input  class='val' type='text' style='width: 108px' value=''>  <input onclick='deletedMapingTr(this);' style='margin-top: -1px;' type='button' value=' - '></td></tr>")
+    }
+    //全国延迟测试网络-删除IP条目
+    function deletedMapingTr(nowTr){
+        $(nowTr).parent().parent().remove();
+    }
+    //保存配置
+    function saveconf(){
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        //config = {}
+        config["Name"] = $("#selfAgentName").html();
+        config["Addr"] = $("#selfAgentAddr").html();
+        config["Mode"]["Type"]="local"
+        config["Mode"]["Endpoint"]=""
+        config["Mode"]["LastSuccTime"]=""
+        config["Base"]["Timeout"]= parseInt($("#Timeout").val());
+        config["Base"]["Archive"]= parseInt($("#Archive").val());
+        config["Base"]["Refresh"]= parseInt($("#Refresh").val());
+        config["Topology"]["Tsound"] = $("#Tsound").val();
+        config["Topology"]["Tline"] = $("#Tline").val();
+        config["Topology"]["Tsymbolsize"] = $("#Tsymbolsize").val();
+        config["Alert"]["EmailHost"] = $("#EmailHost").val();
+        config["Alert"]["SendEmailAccount"] = $("#SendEmailAccount").val();
+        config["Alert"]["SendEmailPassword"] = $("#SendEmailPassword").val();
+        config["Alert"]["RevcEmailList"] = $("#RevcEmailList").val();
+        config["Authiplist"] = $("#Authiplist").val();
+        config["Toollimit"] = parseInt($("#Toollimit").val());
+        data = {}
+        data['config'] = JSON.stringify(config)
+        data['password'] = $(".password").val()
+        $.ajax({
+            dataType: "json",
+            type: 'POST',
+            url: "/api/saveconfig.json",
+            data: data,
+            success: function( retdata ) {
+                if (retdata["status"]=="true"){
+                    alert("系统配置保存成功!")
+                    window.location.reload();
+                }else{
+                    alert("系统配置保存失败 ("+retdata["info"]+")")
+                }
+            },
+            timeout: 10*1000
+        }).fail( function( xhr, status ) {
+            alert("系统配置保存失败 (API错误!)")
+        });
+    }
+    //云模式保存配置
+    function savecloudconf(){
+        //config = {}
+        endpoint = $("#Endpoint").val();
+        $.getJSON("/api/proxy.json?g="+endpoint+"",function(config){
+            config["Mode"]["Endpoint"] = $("#Endpoint").val();
+            config["Mode"]["Type"] = "cloud";
+            //config["Mode"]["Status"] = "true";
+            config["Addr"] = $("#CloudSelfAddr").val();
+            config["Name"] = $("#CloudSelfName").val();
+            selfFlag = false
+            if(!config.hasOwnProperty("Network")){
+                alert("远程配置文件解析错误!")
+                return false
+            }
+            $.each(config["Network"], function(Addr, agentconfig){
+                //console.log("1 "+config["Addr"]+config["Name"])
+                //console.log("2 "+agentconfig["Addr"]+agentconfig["Name"]+Addr)
+                if(agentconfig["Addr"]==config["Addr"] && agentconfig["Name"]==config["Name"]&& Addr==config["Addr"] ){
+                    selfFlag=true
+                    return false
+                }
+            })
+            if(!selfFlag){
+                alert("配置文件错误,本机节点与Ping节点测试网络信息不匹配!");
+                return
+            }
+            data = {}
+            data['config'] = JSON.stringify(config)
+            data['password'] = $("#CloudPass").val()
+            $.ajax({
+                dataType: "json",
+                type: 'POST',
+                url: "/api/saveconfig.json",
+                data: data,
+                success: function( retdata ) {
+                    if (retdata["status"]=="true"){
+                        alert("系统配置保存成功!")
+                        window.location.reload();
+                    }else{
+                        alert("系统配置保存失败 ("+retdata["info"]+")")
+                    }
+                },
+                timeout: 10*1000
+            }).fail( function( xhr, status ) {
+                alert("系统配置保存失败 (API错误!)")
+            });
+        }).fail(function (xhr, status) {
+            //alert("123")
+            alert(xhr.responseText);
+        });
+    }
+    //选择是否为SmartPing
+    function checkSP(obj){
+        Addr=$(obj).parent().parent().find(".Addr").html()
+        config=$.parseJSON(sessionStorage.getItem("config"))
+        if($(obj).is(":checked")){
+            $(obj).parent().parent().find("a").each(function(i,elm) {
+                $(elm).removeAttr("disabled")
+                config["Network"][Addr]["Smartping"]=true
+            })
+        }else{
+            $(obj).parent().parent().find("a").each(function(i,elm){
+              $(elm).attr("disabled","true")
+                config["Network"][Addr]["Smartping"]=false
+                config["Network"][Addr]["Ping"]=new Array()
+                config["Network"][Addr]["Tools"]=new Object()
+                config["Network"][Addr]["Tools"]["Icmpping"]=new Array()
+                config["Network"][Addr]["Topology"]=new Array()
+            })
+        }
+        sessionStorage.setItem("config",JSON.stringify(config))
+    }
+    $(function(){
+        $(".ttip").tooltip();
+        var config = new Object()
+        $.getJSON("/api/config.json",function(result){
+            AgentMode(result["Mode"])
+            if(result["Mode"]["Type"]=="cloud"){
+                $("#LastSuccTimeStr").html("<span class='alert-success' style='padding:10px;'>最近一次成功同步时间:"+result["Mode"]["LastSuccTime"]+"</span><br/>")
+            }else{
+                if( tp!="?dcloud" ) {
+                    $(".localmode").show();
+                    $(".cloudmode").remove();
+                }
+            }
+            $("#agentname").html(" ("+result["Name"]+")")
+            sessionStorage.setItem("config",JSON.stringify(result))
+            config=result
+            $("#verion").html("Version: "+config["Ver"])
+            $("#Timeout").val(config["Base"]["Timeout"]);
+            $("#Refresh").val(config["Base"]["Refresh"]);
+            $("#Archive").val(config["Base"]["Archive"]);
+            $("#Tsound").val(config["Topology"]["Tsound"]);
+            $("#Tline").val(config["Topology"]["Tline"]);
+            $("#Tsymbolsize").val(config["Topology"]["Tsymbolsize"]);
+            $("#EmailHost").val(config["Alert"]["EmailHost"]);
+            $("#SendEmailAccount").val(config["Alert"]["SendEmailAccount"]);
+            $("#SendEmailPassword").val(config["Alert"]["SendEmailPassword"]);
+            $("#RevcEmailList").val(config["Alert"]["RevcEmailList"]);
+            $("#Authiplist").val(config["Authiplist"]);
+            $("#Toollimit").val(config["Toollimit"]);
+            selfconfig=config["Network"][config["Addr"]]
+            $("#CloudSelfName").val(selfconfig["Name"])
+            $("#CloudSelfAddr").val(selfconfig["Addr"])
+            if(config["Mode"]["Endpoint"]!=""){
+                $("#Endpoint").val(config["Mode"]["Endpoint"]);
+            }
+            $(".selfagent").html("<tr>" +
+                "<td class='selfAgentName' id='selfAgentName'>"+selfconfig["Name"]+"</td><td class='selfAgentAddr Addr' id='selfAgentAddr'>"+selfconfig["Addr"]+"</td>" +
+                "<td style='text-align:center'><input type='checkbox' checked disabled></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='pingcfg(this,false)'>配置</a></td>" +
+                "<td style='text-align:center'><a class='btn btn-mini' onclick='topocfg(this,false)'>配置</a></td>" +
+                //"<td style='text-align:center'><a class='btn btn-mini' onclick='toolcfg(this,false)'>配置</a></td>" +
+                "<td style='text-align: center'><i onclick='editSelfagentModal(false);' class='icon-large icon-edit'></i>" +
+                "</tr>")
+            $.each(config["Network"], function(Addr, agentconfig){
+                if(config["Addr"]!=Addr){
+                    checked=""
+                    disabled="disabled"
+                    if(agentconfig["Smartping"]){
+                        checked="checked"
+                        disabled=""
+                    }
+                    $(".agentlist").append("<tr class='agentlist-"+agentconfig["Addr"]+"'>" +
+                        "<td>"+agentconfig["Name"]+"</td><td class='Addr'>"+agentconfig["Addr"]+"</td>" +
+                        "<td style='text-align:center'><input type='checkbox' "+checked+" onclick='checkSP(this)'></td>" +
+                        "<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='pingcfg(this,false)'>配置</a></td>" +
+                        "<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='topocfg(this,false)'>配置</a></td>" +
+                        //"<td style='text-align:center'><a class='btn btn-mini' "+disabled+" onclick='toolcfg(this,false)'>配置</a></td>" +
+                        "<td style='text-align: center'><i onclick='delAgent(this,null);' class='icon-large icon-trash'></i></td>" +
+                        "</tr>")
+                }
+            })
+            refreshChinaMap();
+        });
+        $(".allchecked").click(function(){
+            var xz = $(this).prop("checked");
+            var topoCheck=false
+            if($(this).attr("v")=="topoCheck"){
+                topoCheck=true
+            }
+            $(this).parents().children("tbody").children("tr").each(function(i,obj) {
+                $(obj).find('input').attr("checked",xz);
+                if(topoCheck){
+                    enabledTopo($(obj).find('input'))
+                }
+            });
+        });
+    });
+</script>
+
+</body>
+</html>

BIN
html/favicon.ico


+ 350 - 0
html/index.html

@@ -0,0 +1,350 @@
+<!DOCTYPE html>
+<html lang="cn">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner --> 
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li   class="active"><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li><a href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a id="cfgUrl" href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+
+      <div class="row">
+        <div  id="main" class="span10" >
+          <div id="lineChart"></div>
+          <!-- ping charts -->
+        </div>
+        <!-- /span10 -->
+        <div class="span2">
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3 style="margin-top: -5px;">节点列表 </h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <thead></thead>
+                <tbody class="agentlist"></tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+        </div>
+      </div>
+
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner -->
+</div>
+<!-- /main -->
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+
+<div id="charts" class="modal fade" tabindex="-1" style="width: 830px;height:500px;">
+  <input type="hidden" id="pannelurl" value=""/>
+  <div class="modal-dialog">
+    <div class="modal-content" >
+        <div style="text-align:center;padding-top:10px;">
+          <b>StartTime:</b>&nbsp;<input id="starttime" type='text'  onClick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm'})" style="width:120px;" >
+          <b>EndTime:</b>&nbsp;<input id="endtime" type='text'  onClick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm'})"  style="width:120px;" >
+          &nbsp;<input class="sgraph" type="button" value="Submit" />
+        </div>
+        <div class="modal-body" id="pannel-show" style="width: 800px;height:500px;">
+      </div>
+    </div><!-- /.modal-content -->
+  </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
+
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/echarts.min.js"></script>
+<script language="JavaScript" type="text/javascript" src="assets/js/My97DatePicker/WdatePicker.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    //getLineChart("");
+    function changeAgent(n,url){
+        $(".alerticon-" + n).addClass("icon-spinner").addClass("icon-spin").addClass("animated");
+        getLineChart(url)
+    }
+    function getLineChart(url){
+      if(url!=""){
+          url="/api/proxy.json?g="+url
+      }
+      $.getJSON(url+"/api/config.json",function(result){
+          $("#lineChart").html("");
+          $(".agentlist").html("");
+          $("#agentname").html(" ("+result["Name"]+")")
+          AgentMode(result["Mode"])
+          $("#verion").html("Version: "+result["Ver"])
+          $.each(result["Network"], function(i, network){
+              if(network["Smartping"]){
+                  $(".agentlist").append("<tr><td><i class='alerticon-" + network["Name"] + "'></i>&nbsp;" +
+                      "<a onclick=changeAgent('" + network["Name"] + "','http://" + network["Addr"] + ":" + result["Port"] + "')>" + network["Name"] + "</a></td></tr>");
+              }
+          });
+          $.each(result["Network"][result["Addr"]]["Ping"], function(i, mAddr){
+              $("#lineChart").append("<div class='img showcharts' " +
+                  "style='text-align:center;float:left;width:30.3%;height:130px;margin-right:3%;margin-top:20px;;background-size:100% 100%;' " +
+                  "imgsrc='/api/graph.png?g=http://"+result["Addr"]+":"+result["Port"]+"/api/ping.json?ip="+mAddr+"' " +
+                  "apiurl='/api/proxy.json?g=http://"+result["Addr"]+":"+result["Port"]+"/api/ping.json?ip="+mAddr+"' >" +
+                  "<b style='font-color:#333333'>"+result["Name"]+"->"+result["Network"][mAddr]["Name"]+"</b></div>");
+          });
+          layzeImg();
+      }).fail(function (xhr, status) {
+          alert(xhr.responseText);
+          Refresh();
+      });
+    }
+    $("#main").on("click",".showcharts",function(){
+        //window.clearInterval(int)
+        var modalWidth = $("#charts").width();
+        var left = "-" + parseInt(modalWidth) / 2 + "px";
+        $('#charts').modal('show').css({"margin-left":left});
+        url=$(this).attr("apiurl");
+        $("#pannelurl").attr("value",url);
+        $.get(url).done(function (data) {
+            //var data = JSON.parse(data);
+            myChart.setOption({
+                xAxis: {
+                    data: data.lastcheck
+                },
+                series: [{
+                    name: 'maxDelay',
+                    data: data.maxdelay
+                },
+                    {
+                        name: 'minDelay',
+                        data: data.mindelay
+                    },
+                    {
+                        name: 'avgDelay',
+                        data: data.avgdelay
+                    },
+                    {
+                        name: 'lossPk',
+                        data: data.losspk
+                    }]
+            });
+        });
+    });
+    $(".sgraph").click(function(){
+        start   = $("#starttime").val();
+        endtime = $("#endtime").val();
+        url = $("#pannelurl").attr("value");
+        $.get(url+"%26starttime="+start+"%26endtime="+endtime).done(function (data) {
+            //console.log(data)
+            myChart.setOption({
+                xAxis: {
+                    data: data.lastcheck
+                },
+                series: [{
+                    name: 'maxDelay',
+                    data: data.maxdelay
+                },
+                    {
+                        name: 'minDelay',
+                        data: data.mindelay
+                    },
+                    {
+                        name: 'avgDelay',
+                        data: data.avgdelay
+                    },
+                    {
+                        name: 'lossPk',
+                        data: data.losspk
+                    }]
+            });
+        });
+    });
+    opt={
+        title: {
+            text: ''
+        },
+        legend: {
+            data:['maxDelay','avgDelay','minDelay','lossPk'],
+            selected: {
+                'maxDelay' : false,
+                'minDelay' : false
+            }
+        },
+        toolbox: {
+            feature: {
+                dataView: {},
+                saveAsImage: {
+                    pixelRatio: 2
+                }
+            }
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        xAxis: {
+            data: []
+        },
+        dataZoom: [{}],
+        yAxis: [{
+            type: 'value',
+            name: 'Delay',
+            position: 'left'
+        }, {
+            type: 'value',
+            name: 'Package(LOSS)',
+            min: 0,
+            max: 100,
+            position: 'right',
+            axisLabel: {
+                formatter: '{value} %'
+            }
+        }],
+        series: [{
+            name: 'maxDelay',
+            type: 'line',
+            animation: false,
+            areaStyle: {
+                normal: {}
+            },
+            lineStyle: {
+                normal: {
+                    width: 0
+                }
+            },
+            data: []
+        },
+            {
+                name: 'minDelay',
+                type: 'line',
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                },
+                data: []
+            },
+            {
+                name: 'avgDelay',
+                type: 'line',
+                data: [],
+                itemStyle: {
+                    normal: {
+                        color : '#00CC66'
+                    }
+                },
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                }
+            },
+            {
+                name: 'lossPk',
+                type: 'line',
+                yAxisIndex: 1,
+                data: [],
+                itemStyle: {
+                    normal: {
+                        color : '#FF0000'
+                    }
+                },
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                }
+            }]
+    }
+    var myChart = echarts.init(document.getElementById('pannel-show'));
+    myChart.setOption(opt);
+    function layzeImg() {
+        var aImg = document.querySelectorAll('div.img');
+        var len = aImg.length;
+        var n = 0;
+        for (var i = n; i < len; i++) {
+            if (aImg[i].offsetTop < document.documentElement.clientHeight + (document.body.scrollTop || document.documentElement.scrollTop)) {
+                if (aImg[i].style.backgroundImage == '') {
+                    aImg[i].style.backgroundImage = "url('"+aImg[i].getAttribute('imgsrc')+"')";
+                }
+                n = i + 1;
+            }
+        }
+        window.onscroll = function () {
+            for (var i = n; i < len; i++) {
+                if (aImg[i].offsetTop < document.documentElement.clientHeight + (document.body.scrollTop || document.documentElement.scrollTop)) {
+                    if (aImg[i].style.backgroundImage == '') {
+                        aImg[i].style.backgroundImage = "url('"+aImg[i].getAttribute('imgsrc')+"')";
+                    }
+                    n = i + 1;
+                }
+            }
+        };
+    };
+</script>
+</body>
+</html>

+ 326 - 0
html/mapping.html

@@ -0,0 +1,326 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li><a href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li class="active"><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span10">
+          <span style="float: right"><input class="Wdate" id="pkdata" onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',onpicked:pickedFunc})" style="width:120px;position:absolute;margin-left: -120px;z-index: 99;"></span>
+          <div id="echarts-map" style="width: 100%;"></div>
+          <input type="hidden" id="agent" value="">
+        </div>
+        <!-- /span10 -->
+        <div class="span2">
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>节点列表</h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <tbody class="agentlist">
+
+                </tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster --> 
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/echarts-all.js"></script>
+<script language="JavaScript" type="text/javascript" src="assets/js/My97DatePicker/WdatePicker.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    function pickedFunc(){
+        d=$("#pkdata").val()
+        var url = $("#agent").val();
+        if(url!=""){
+            url="/api/proxy.json?g="+url
+        }
+        $.getJSON(url+"/api/mapping.json?d="+d,function(result){
+            option = {
+                title : {
+                    text: result["text"],
+                    subtext: result["subtext"],
+                    x:'center'
+                },
+                tooltip : {
+                    trigger: 'item'
+                },
+                legend: {
+                    orient: 'vertical',
+                    x:'left',
+                    data:['电信','联通','移动'],
+                },
+                dataRange: {
+                    min: 0,
+                    max: 200,
+                    x: 'left',
+                    y: 'bottom',
+                    text:['高','低'],
+                    splitList: [
+                        {start: 200, color: 'red'},
+                        {start: 150, end: 200},
+                        {start: 100, end: 150},
+                        {start: 50, end: 100, color: 'Green'},
+                        {start: 0, end: 50,color: 'DarkGreen'}
+                    ],
+                    color: ['#E0022B', '#E09107', '#A3E00B']
+                },
+                toolbox: {
+                    show: true,
+                    orient : 'vertical',
+                    x: 'right',
+                    y: 'center',
+                    feature : {
+                        mark : {show: true},
+                        dataView : {show: true, readOnly: false},
+                        saveAsImage : {show: true}
+                    }
+                },
+                series : [
+                    {
+                        name: '电信',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["ctcc"]
+                    },
+                    {
+                        name: '联通',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["cucc"]
+                    },
+                    {
+                        name: '移动',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["cmcc"]
+                    }
+                ]
+            };
+            var mainContainer = document.getElementById('echarts-map');
+            var resizeMainContainer = function () {
+                mainContainer.style.height = ($(window).height()-220)+"px";
+            };
+            resizeMainContainer();
+            var mainChart = echarts.init(mainContainer);
+            $(window).on('resize',function(){//
+                resizeMainContainer();
+                mainChart.resize();
+            });
+            mainChart.setOption(option);
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });;
+    }
+    function changeAgent(n,url){
+        $(".alerticon-" + n).addClass("icon-spinner").addClass("icon-spin").addClass("animated");
+        if(url!=""){
+            $("#agent").val(url);
+            url="/api/proxy.json?g="+url
+        }
+        $.getJSON(url+"/api/mapping.json",function(result){
+            option = {
+                title : {
+                    text: result["text"],
+                    subtext: result["subtext"],
+                    x:'center'
+                },
+                tooltip : {
+                    trigger: 'item'
+                },
+                legend: {
+                    orient: 'vertical',
+                    x:'left',
+                    data:['电信','联通','移动'],
+                },
+                dataRange: {
+                    min: 0,
+                    max: 200,
+                    x: 'left',
+                    y: 'bottom',
+                    text:['高','低'],
+                    splitList: [
+                        {start: 1000, color: 'red'},
+                        {start: 300, end: 1000},
+                        {start: 100, end: 300},
+                        {start: 50, end: 100, color: 'DarkGreen'},
+                        {start: 0, end: 50,color: 'Green'}
+                    ],
+                    color: ['#E0022B', '#E09107', '#A3E00B']
+                },
+                toolbox: {
+                    show: true,
+                    orient : 'vertical',
+                    x: 'right',
+                    y: 'center',
+                    feature : {
+                        mark : {show: true},
+                        dataView : {show: true, readOnly: false},
+                        saveAsImage : {show: true}
+                    }
+                },
+                series : [
+                    {
+                        name: '电信',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["ctcc"]
+                    },
+                    {
+                        name: '联通',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["cucc"]
+                    },
+                    {
+                        name: '移动',
+                        type: 'map',
+                        mapType: 'china',
+                        mapValueCalculation:'average',
+                        itemStyle:{
+                            normal:{label:{show:true}},
+                            emphasis:{label:{show:true}}
+                        },
+                        data:result["avgdelay"]["cmcc"]
+                    }
+                ]
+            };
+            var mainContainer = document.getElementById('echarts-map');
+            var resizeMainContainer = function () {
+                mainContainer.style.height = ($(window).height()-220)+"px";
+            };
+            resizeMainContainer();
+            var mainChart = echarts.init(mainContainer);
+            $(window).on('resize',function(){//
+                resizeMainContainer();
+                mainChart.resize();
+            });
+            mainChart.setOption(option);
+            $(".alerticon-" + n).removeClass("icon-spinner").removeClass("icon-spin").removeClass("animated");
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });
+    }
+    $(function(){
+        $("#echarts").height($(window).height()-220);
+        $.getJSON("/api/config.json",function(result){
+            $("#agentname").html(" ("+result["Name"]+")")
+            AgentMode(result["Mode"])
+            $("#verion").html("Version: "+result["Ver"])
+            $.each(result["Network"], function(i, network){
+                //if(field.Agent==true) {
+                if(network["Smartping"]) {
+                    $(".agentlist").append("<tr><td><i class='alerticon-" + network["Name"] + "'></i>&nbsp;<a onclick=changeAgent('" + network["Name"] + "','http://" + network["Addr"] + ":" + result["Port"] + "')>" + network["Name"] + "</a></td></tr>");
+                }
+                //}
+            });
+            changeAgent("","");
+            setTimeout('Refresh()',result["Base"]["Refresh"]*60*1000);
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });
+
+    });
+</script>
+</body>
+</html>

+ 362 - 0
html/reverse.html

@@ -0,0 +1,362 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>SmartPing Dashboard</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+  <meta name="apple-mobile-web-app-capable" content="yes">
+  <link href="assets/css/bootstrap.min.css" rel="stylesheet">
+  <link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+  <link href="assets/css/font-awesome.css" rel="stylesheet">
+  <link href="assets/css/style.css" rel="stylesheet">
+  <link href="assets/css/pages/dashboard.css" rel="stylesheet">
+  <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+  <!--[if lt IE 9]>
+  <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+  <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li  class="active"><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li><a href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /subnavbar-inner -->
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+
+      <div class="row">
+        <div  id="main" class="span10">
+          <!-- ping charts -->
+        </div>
+        <!-- /span10 -->
+        <div class="span2">
+          <div class="widget widget-table action-table">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>节点列表</h3>
+            </div>
+            <!-- /widget-header -->
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <thead>
+                </thead>
+                <tbody class="agentlist">
+
+                </tbody>
+              </table>
+            </div>
+            <!-- /widget-content -->
+          </div>
+        </div>
+      </div>
+
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /main-inner -->
+</div>
+<!-- /main -->
+
+
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 -->
+      </div>
+      <!-- /row -->
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /footer-inner -->
+</div>
+
+<div id="charts" class="modal fade" tabindex="-1" style="width: 830px;height:500px;">
+  <input type="hidden" id="pannelurl" value=""/>
+  <div class="modal-dialog">
+    <div class="modal-content" >
+      <div style="text-align:center;padding-top:10px;">
+        <b>StartTime:</b>&nbsp;<input id="starttime" type='text'  onClick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm'})" style="width:120px;" >
+        <b>EndTime:</b>&nbsp;<input id="endtime" type='text'  onClick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm'})"  style="width:120px;" >
+        &nbsp;<input class="sgraph" type="button" value="Submit" />
+      </div>
+      <div class="modal-body" id="pannel-show" style="width: 800px;height:500px;">
+      </div>
+    </div><!-- /.modal-content -->
+  </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
+
+<!-- /footer -->
+<!-- Le javascript
+================================================== -->
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/echarts.min.js"></script>
+<script language="JavaScript" type="text/javascript" src="assets/js/My97DatePicker/WdatePicker.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    //var int
+    getLineChart("");
+    function changeAgent(n,url){
+        $(".alerticon-" + n).addClass("icon-spinner").addClass("icon-spin").addClass("animated");
+        getLineChart(url)
+    }
+    function getLineChart(url) {
+        if(url!=""){
+            url="/api/proxy.json?g="+url
+        }
+        $.getJSON(url+"/api/config.json", function (result) {
+            $("#main").html("");
+            $(".agentlist").html("");
+            $("#agentname").html(" ("+result["Name"]+")")
+            AgentMode(result["Mode"])
+            //int = setTimeout('Refresh()',result["Alertcycle"]*60*1000);
+            $("#verion").html("Version: " + result["Ver"])
+            $.each(result["Network"], function(i, network){
+                if(network["Smartping"]) {
+                    $(".agentlist").append("<tr><td><i class='alerticon-" + network["Name"] + "'></i>&nbsp;" +
+                        "<a onclick=changeAgent('" + network["Name"] + "','http://" + network["Addr"] + ":" + result["Port"] + "')>" + network["Name"] + "</a></td></tr>");
+                }
+            });
+            $.each(result["Network"], function(rAddr, network){
+                if (rAddr==result["Addr"]){
+                    return true
+                }
+                $.each(network["Ping"], function(i, ping){
+                    if(ping==result["Addr"]){
+                        $("#main").append("<div style='text-align:center;float:left;width:30.3%;height:130px;margin-right:3%;margin-top:20px;background-size:100% 100%;' " +
+                            "class='img showcharts' imgsrc='/api/graph.png?g=http://"+rAddr+":"+result["Port"]+"/api/ping.json?ip="+result["Addr"]+"' " +
+                            "apiurl='/api/proxy.json?g=http://" + rAddr + ":" + result['Port'] + "/api/ping.json?ip=" + result['Addr'] + "' " +
+                            "><b style='font-color:#333333'>"+network["Name"]+"->"+result["Name"]+"</b></div>");
+                    }
+                })
+            });
+            layzeImg()
+            $("#main").on("click", ".showcharts", function () {
+                //window.clearInterval(int)
+                var modalWidth = $("#charts").width();
+                var left = "-" + parseInt(modalWidth) / 2 + "px";
+                $('#charts').modal('show').css({"margin-left": left});
+                url = $(this).attr("apiurl");
+                $("#pannelurl").attr("value", url);
+                $.get(url).done(function (data) {
+                    //var data = JSON.parse(data);
+                    myChart.setOption({
+                        xAxis: {
+                            data: data.lastcheck
+                        },
+                        series: [{
+                            name: 'maxDelay',
+                            data: data.maxdelay
+                        },
+                            {
+                                name: 'minDelay',
+                                data: data.mindelay
+                            },
+                            {
+                                name: 'avgDelay',
+                                data: data.avgdelay
+                            },
+                            {
+                                name: 'lossPk',
+                                data: data.losspk
+                            }]
+                    });
+                });
+            });
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });
+    }
+    $(".sgraph").click(function(){
+        start   = $("#starttime").val();
+        endtime = $("#endtime").val();
+        url = $("#pannelurl").attr("value");
+        $.get(url+"%26starttime="+start+"%26endtime="+endtime).done(function (data) {
+            //var data = JSON.parse(data);
+            myChart.setOption({
+                xAxis: {
+                    data: data.lastcheck
+                },
+                series: [{
+                    name: 'maxDelay',
+                    data: data.maxdelay
+                },
+                    {
+                        name: 'minDelay',
+                        data: data.mindelay
+                    },
+                    {
+                        name: 'avgDelay',
+                        data: data.avgdelay
+                    },
+                    {
+                        name: 'lossPk',
+                        data: data.losspk
+                    }]
+            });
+        });
+    });
+    opt={
+        title: {
+            text: ''
+        },
+        legend: {
+            data:['maxDelay','avgDelay','minDelay','lossPk'],
+            selected: {
+                'maxDelay' : false,
+                'minDelay' : false
+            }
+        },
+        toolbox: {
+            feature: {
+                dataView: {},
+                saveAsImage: {
+                    pixelRatio: 2
+                }
+            }
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        xAxis: {
+            data: []
+        },
+        dataZoom: [{}],
+        yAxis: [{
+            type: 'value',
+            name: 'Delay',
+            position: 'left'
+        }, {
+            type: 'value',
+            name: 'Package(LOSS)',
+            min: 0,
+            max: 100,
+            position: 'right',
+            axisLabel: {
+                formatter: '{value} %'
+            }
+        }],
+        series: [{
+            name: 'maxDelay',
+            type: 'line',
+            animation: false,
+            areaStyle: {
+                normal: {}
+            },
+            lineStyle: {
+                normal: {
+                    width: 0
+                }
+            },
+            data: []
+        },
+            {
+                name: 'minDelay',
+                type: 'line',
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                },
+                data: []
+            },
+            {
+                name: 'avgDelay',
+                type: 'line',
+                data: [],
+                itemStyle: {
+                    normal: {
+                        color : '#00CC66'
+                    }
+                },
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                }
+            },
+            {
+                name: 'lossPk',
+                type: 'line',
+                yAxisIndex: 1,
+                data: [],
+                itemStyle: {
+                    normal: {
+                        color : '#FF0000'
+                    }
+                },
+                animation: false,
+                areaStyle: {
+                    normal: {}
+                },
+                lineStyle: {
+                    normal: {
+                        width: 0
+                    }
+                }
+            }]
+    }
+    var myChart = echarts.init(document.getElementById('pannel-show'));
+    myChart.setOption(opt);
+    function layzeImg() {
+        var aImg = document.querySelectorAll('div.img');
+        var len = aImg.length;
+        var n = 0;
+        for (var i = n; i < len; i++) {
+            if (aImg[i].offsetTop < document.documentElement.clientHeight + (document.body.scrollTop || document.documentElement.scrollTop)) {
+                if (aImg[i].style.backgroundImage == '') {
+                    aImg[i].style.backgroundImage = "url('"+aImg[i].getAttribute('imgsrc')+"')";
+                }
+                n = i + 1;
+            }
+        }
+        window.onscroll = function () {
+            for (var i = n; i < len; i++) {
+                if (aImg[i].offsetTop < document.documentElement.clientHeight + (document.body.scrollTop || document.documentElement.scrollTop)) {
+                    if (aImg[i].style.backgroundImage == '') {
+                        aImg[i].style.backgroundImage = "url('"+aImg[i].getAttribute('imgsrc')+"')";
+                    }
+                    n = i + 1;
+                }
+            }
+        };
+    };
+</script>
+</body>
+</html>

+ 185 - 0
html/tools.html

@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li><a href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li  class="active"><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12 form-horizontal" style="text-align: center">
+          <div class="control-group">
+            <div class="controls" style="padding:30px 0 0 0;margin: 0">
+              <div class="input-append">
+                <select style="width:100px;">
+                  <option>ICMP PING</option>
+                </select>
+                <input class="span4 m-wrap"  type="text" id="target">
+                <button class="btn" type="button" onclick="checkit()">检测一下!</button>
+              </div>
+            </div>	<!-- /controls -->
+          </div> <!-- /control-group -->
+
+          <table class="table table-striped table-bordered">
+            <thead>
+            <tr>
+              <th style="width:20px;"><input type='checkbox' checked id="allchecked"></th>
+              <th style="width:200px;"> 节点名称 </th>
+              <th> 解析IP </th>
+              <th> 发送 </th>
+              <th> 接收 </th>
+              <th> 丢弃 </th>
+              <th> 最大时间 </th>
+              <th> 最小时间 </th>
+              <th> 平均时间 </th>
+              <th style="width:15px;"> </th>
+            </tr>
+            </thead>
+            <tbody class="agentlist-tools" id="list">
+
+            </tbody>
+          </table>
+        </div>
+
+        <!-- /widget-content -->
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster --> 
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/echarts.min.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+  function checkit(){
+      target = $("#target").val();
+      if(target==""){
+          alert("Target Error!")
+          return
+      }
+      $('#list tr').each(function(i){
+          checked = $($(this).children('td')[0]).children("input").is(":checked");
+          if(checked){
+              Addr = $($(this).children('td')[1]).attr("v");
+              var thisTr = $(this);
+              $(thisTr.children('td')[2]).html("");
+              $(thisTr.children('td')[3]).html("");
+              $(thisTr.children('td')[4]).html("");
+              $(thisTr.children('td')[5]).html("");
+              $(thisTr.children('td')[6]).html("");
+              $(thisTr.children('td')[7]).html("");
+              $(thisTr.children('td')[8]).html("");
+              $(thisTr.children('td')[9]).html("<i class='icon-spinner icon-spin animated erricon'  data-toggle='tooltip' title='' data-placement='bottom'></i>")
+              var p = $.getJSON("/api/proxy.json?t=10&g=http://"+Addr+"/api/tools.json?t="+target,function(result){
+                  if(result.status=="true"){
+                      $(thisTr.children('td')[2]).html(result.ip);
+                      $(thisTr.children('td')[3]).html(result.ping.SendPk);
+                      $(thisTr.children('td')[4]).html(result.ping.RevcPk);
+                      $(thisTr.children('td')[5]).html(result.ping.LossPk+"%");
+                      $(thisTr.children('td')[6]).html(result.ping.MaxDelay.toFixed(2)+"ms");
+                      $(thisTr.children('td')[7]).html(result.ping.MinDelay.toFixed(2)+"ms");
+                      $(thisTr.children('td')[8]).html(result.ping.AvgDelay.toFixed(2)+"ms");
+                      $(thisTr.children('td')[9]).html("")
+                      //thisTr.addClass("danger")
+                  }else{
+                      $(thisTr.children('td')[9]).html("<i class='icon-time animated erricon'  data-toggle='tooltip' title='"+result.error+"' data-placement='bottom'></i>")
+                      $(".erricon").tooltip();
+                  }
+              }).fail(function (xhr, status) {
+                  $(thisTr.children('td')[9]).html("<i class='icon-warning-sign animated erricon' data-toggle='tooltip' title='"+xhr.statusText+":"+xhr.responseText+"' data-placement='bottom'></i>")
+                  $(".erricon").tooltip();
+              });
+          }else{
+              $(this).remove()
+          }
+      });
+  }
+    $(function(){
+        $("#allchecked").click(function(){
+            var xz = $(this).prop("checked");
+            $('#list tr').each(function(i) {
+                $($(this).children('td')[0]).children("input").attr("checked",xz);
+            });
+        });
+        $.getJSON("/api/config.json",function(result){
+            $("#target").val(result["Addr"]);
+            $("#agentname").html(" ("+result["Name"]+")")
+            AgentMode(result["Mode"])
+            $("#verion").html("Version: "+result["Ver"])
+            $.each(result["Network"], function(i, network){
+                if(network["Smartping"]) {
+                    $(".agentlist-tools").append("<tr><td><input type='checkbox' checked></td><td v='" + network["Addr"] + ":" + result["Port"] + "'>" + network["Name"] + "</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>");
+                }
+            });
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });
+    });
+</script>
+</body>
+</html>

+ 276 - 0
html/topology.html

@@ -0,0 +1,276 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>SmartPing Dashboard</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<meta name="apple-mobile-web-app-capable" content="yes">
+<link href="assets/css/bootstrap.min.css" rel="stylesheet">
+<link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
+<link href="assets/css/font-awesome.css" rel="stylesheet">
+<link href="assets/css/style.css" rel="stylesheet">
+<link href="assets/css/pages/dashboard.css" rel="stylesheet">
+  <link href="assets/css/pages/plans.css" rel="stylesheet">
+<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+<!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+</head>
+<body>
+<div class="navbar navbar-fixed-top">
+  <div class="navbar-inner">
+    <div class="container">
+      <span id="cloudbrand" class="brand" style="margin-right: -15px;"></span><a class="brand" href="index.html">SmartPing Dashbord<span id="agentname"></span></a>
+      <span id="banner_last_ck_time" class="pull-right " style="margin-top: 10px;"></span>
+    </div>
+    <!-- /container -->
+  </div>
+  <!-- /navbar-inner -->
+</div>
+<!-- /navbar -->
+<div class="subnavbar">
+  <div class="subnavbar-inner">
+    <div class="container">
+      <ul class="mainnav">
+        <li><a href="index.html"><i class="icon-mail-forward"></i><span>正向Ping</span> </a> </li>
+        <li><a href="reverse.html"><i class="icon-mail-reply"></i><span>反向Ping</span> </a> </li>
+        <li  class="active"><a href="topology.html"><i class="icon-bar-chart"></i><span>Ping拓扑</span> </a> </li>
+        <li><a href="mapping.html"><i class="icon-map-marker"></i><span>全国延迟</span> </a> </li>
+        <li ><a href="tools.html"><i class="icon-wrench"></i><span>检测工具</span> </a> </li>
+        <li><a  id="cfgUrl"  href="config.html"><i class="icon-cog"></i><span>系统配置</span> </a> </li>
+      </ul>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /subnavbar-inner --> 
+</div>
+<!-- /subnavbar -->
+<div class="main" style="margin-top:-20px;">
+  <div class="main-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span10">
+          <div id="echarts" style="width:100%;"></div>
+        </div>
+
+        <div class="span2">
+          <div class="pricing-plans plans-1" style="margin-left: 5px;">
+            <div class="plan-container">
+              <div class="plan green">
+                <div class="plan-header" >
+                  <div class="plan-title" onclick="javascript:window.location.href='alerts.html'">
+                    <i class="icon-warning-sign"></i> 查看报警记录
+                  </div> <!-- /plan-title -->
+                </div> <!-- /plan-header -->
+              </div> <!-- /plan -->
+            </div> <!-- /plan-container -->
+          </div>
+          <!--
+          <div style="text-align: center;margin-bottom: 15px;">
+            <button class="alert alert-danger" style="width:100%;height:50px;margin-bottom: -5px;text-align: center" onclick="javascript:window.location.href='alerts.html'"><i class="icon-warning-sign"></i> 查看报警记录</button>
+          </div>
+          -->
+          <div class="widget widget-table action-table" style="margin-top: 60px;">
+            <div class="widget-header"> <i class="icon-th-list"></i>
+              <h3>拓扑列表</h3>
+            </div>
+            <div class="widget-content">
+              <table class="table table-striped table-bordered">
+                <tbody class="agentlist"></tbody>
+              </table>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /main-inner --> 
+</div>
+<!-- /main -->
+
+<div class="footer">
+  <div class="footer-inner">
+    <div class="container">
+      <div class="row">
+        <div class="span12"> &copy; 2017 - 2020 <a target="_blank" href="http://smartping.org" >SmartPing.org</a> <span style="float:right" id="verion"></span></div>
+        <!-- /span12 --> 
+      </div>
+      <!-- /row --> 
+    </div>
+    <!-- /container --> 
+  </div>
+  <!-- /footer-inner --> 
+</div>
+<!-- /footer --> 
+<!-- Le javascript
+================================================== --> 
+<!-- Placed at the end of the document so the pages load faster --> 
+<script src="assets/js/jquery-1.7.2.min.js"></script>
+<script src="assets/js/bootstrap.js"></script>
+<script src="assets/js/echarts.min.js"></script>
+<script src="assets/js/funcs.js"></script>
+<script>
+    $(function(){
+        var ajaxCnt = 0
+        $("#echarts").height($(window).height()-220);
+        $.getJSON("/api/config.json",function(result){
+            $("#agentname").html(" ("+result["Name"]+")")
+            AgentMode(result["Mode"])
+            $("#verion").html("Version: "+result["Ver"])
+            setTimeout('Refresh()',result["Base"]["Refresh"]*60*1000);
+            var dataarea = [];
+            var dataline = [];
+            option = {
+                tooltip: {
+                    show: true
+                },
+                animationDurationUpdate: 1500,
+                animationEasingUpdate: 'quinticInOut',
+                series : [
+                    {
+                        type: 'graph',
+                        layout: 'circular',
+                        symbolSize: result["Topology"]["Tsymbolsize"],
+                        focusNodeAdjacency:true,
+                        roam: true,
+                        label: {
+                            normal: {
+                                show: true
+                            }
+                        },
+                        edgeSymbol: ['circle', 'arrow'],
+                        edgeSymbolSize: [3, 15],
+                        edgeLabel: {
+                            normal: {
+                                textStyle: {
+                                    fontSize: 15
+                                }
+                            }
+                        },
+                        data : dataarea,
+                        links: dataline,
+                        lineStyle: {
+                            normal: {
+                                opacity: 0.9,
+                                width: result["Topology"]["Tline"],
+                                curveness: 0
+                            }
+                        }
+                    }
+                ]
+            };
+            var myChart = echarts.init(document.getElementById('echarts'));
+            myChart.setOption(option);
+            FromObj = new Object();
+            FromToObj = new Object();
+            $.each(result["Network"], function(i, network){
+                ArrObj = new Object()
+                ArrObj["Name"]=network["Name"]
+                ArrObj["Addr"]=network["Addr"]
+                ArrObj["Type"]="To"
+                ArrObj["Color"]= "green"
+                if(network["Topology"].length>0){
+                    ArrObj["Color"]= "gray"
+                    ArrObj["Type"]="From"
+                }
+                FromObj[network["Addr"]]=ArrObj
+                $.each(network["Topology"], function(i, topology){
+                    ArrObj = new Object()
+                    ArrObj["From"] = network["Name"]
+                    ArrObj["To"] = result["Network"][topology["Addr"]]["Name"]
+                    ArrObj["Curveness"] = 0.2
+                    ArrObj["Color"] ="gray"
+                    FromToObj[network["Addr"]+"-"+topology["Addr"]]= ArrObj
+                })
+            });
+            $.each(FromObj, function(i, network){
+                if(network["Type"]=="To"){
+                    return true
+                }
+                ajaxCnt = ajaxCnt + 1
+                $(".agentlist").append("<tr><td><i class='icon-spinner icon-spin animated alerticon-" + network["Name"] + "'  data-toggle='tooltip' title='' data-placement='bottom'></i>&nbsp;" + network["Name"] + "</td></tr>");
+            });
+
+            $.each(FromObj,function(i,FromObjInfo){
+                if(FromObjInfo["Type"]=="To"){
+                    return true
+                }
+                $.ajax({
+                    dataType: "json",
+                    url: '/api/proxy.json?g=http://' + FromObjInfo["Addr"] + ':' + result['Port'] + '/api/topology.json',
+                    success: function (topodata) {
+                        ajaxCnt = ajaxCnt - 1
+                        $(".alerticon-" + FromObjInfo["Name"] + "").remove();
+                        $.each(topodata,function(ToAddr,Flag){
+                            if(FromToObj.hasOwnProperty(FromObjInfo["Addr"]+"-"+ToAddr)){
+                                if(Flag == "true"){
+                                    FromToObj[FromObjInfo["Addr"]+"-"+ToAddr]["Curveness"]=0
+                                    FromToObj[FromObjInfo["Addr"]+"-"+ToAddr]["Color"]="green"
+                                }else{
+                                    FromToObj[FromObjInfo["Addr"]+"-"+ToAddr]["Curveness"]=0.2
+                                    FromToObj[FromObjInfo["Addr"]+"-"+ToAddr]["Color"]="red"
+                                    if ($("#alert").length < 1) {
+                                        $(".main").append('<audio id="alert" style="display:none"  autoplay="autoplay"  controls="controls" loop="loop"><source src="' + result["Topology"]["Tsound"] + '" type="audio/mp3"  /></audio>');
+                                    }
+                                    //console.log(FromObjInfo["Addr"]+"-"+ToAddr)
+                                }
+                            }
+                        })
+                        FromObj[FromObjInfo["Addr"]]["Color"]="green"
+                        //console.log(ajaxCnt)
+                        if(ajaxCnt==0){
+                            Dawr()
+                        }
+                    },
+                    timeout: result["Base"]["Timeout"] * 1000
+                }).fail(function (xhr, status) {
+                    ajaxCnt = ajaxCnt - 1
+                    $(".alerticon-" + FromObjInfo["Name"] + "").removeClass("icon-spinner").removeClass("icon-spin").removeClass("animated");
+                    $(".alerticon-" + FromObjInfo["Name"] + "").addClass("icon-warning-sign");
+                    $(".alerticon-" + FromObjInfo["Name"] + "").attr("title",xhr.responseText);
+                    $(".alerticon-" + FromObjInfo["Name"] + "").tooltip();
+                    FromObj[FromObjInfo["Addr"]]["Color"]="red"
+                    if ($("#alert").length < 1) {
+                       $(".main").append('<audio id="alert" style="display:none"  autoplay="autoplay"  controls="controls" loop="loop"><source src="' + result["Topology"]["Tsound"]+ '" type="audio/mp3"  /></audio>');
+                    }
+                    if(ajaxCnt==0){
+                        Dawr()
+                    }
+                });
+            })
+            function Dawr(){
+                //console.log(FromObj)
+                $.each(FromObj,function(i,Area){
+                    dataarea.push({
+                        name: Area["Name"],
+                        draggable: "true",
+                        itemStyle: {
+                            normal: {
+                                color: Area["Color"]
+                            }
+                        }
+                    });
+                })
+                $.each(FromToObj,function(i,Line){
+                    //console.log("=== ",i,Line)
+                    dataline.push({
+                        source: Line["From"],
+                        target: Line["To"],
+                        lineStyle: {
+                            normal: {curveness: Line["Curveness"], color: Line["Color"]}
+                        }
+                    })
+                })
+                myChart.setOption(option);
+            }
+
+        }).fail(function (xhr, status) {
+            alert(xhr.responseText);
+            Refresh();
+        });
+
+    });
+</script>
+</body>
+</html>

+ 25 - 59
main.go

@@ -1,70 +1,36 @@
 package main
 
 import (
-	"github.com/cihub/seelog"
-	"net"
-	"node_monitor/g"
-	"node_monitor/nettools"
+	"github.com/gin-gonic/gin"
+	"net/http"
 	"runtime"
-	"time"
 )
 
+func HandleIndex(ctx *gin.Context) {
+
+	ctx.HTML(http.StatusOK, "index.html", gin.H{
+		"title": "Main website",
+	})
+}
 func main() {
 	runtime.GOMAXPROCS(runtime.NumCPU())
+	r := gin.Default()
+	r.LoadHTMLGlob("html/*")
+	r.Static("/assets", "./assets")
+	r.GET("/", HandleIndex)
+	r.GET("/add", func(context *gin.Context) {
+		context.HTML(http.StatusOK, "add.html", gin.H{
+			"title": "Main website",
+		})
+	})
 
-	var ipSlice []string
-	ipSlice = append(ipSlice, "kdvkr-02.xyz")
-	ipSlice = append(ipSlice, "kdvkr-04.xyz")
-	stat := g.PingSt{}
-	stat.MinDelay = -1
-	lossPK := 0
-	for i := 0; i < len(ipSlice); i++ {
-		addr, err := net.ResolveIPAddr("ip", ipSlice[i])
-		seelog.Info("Start Ping " + ipSlice[i] + "..")
-		if err == nil {
-			for i := 0; i < 10; i++ {
-				starttime := time.Now().UnixNano()
-				//tcpping := nettools.NewTcpPing(addr.String(), 22, time.Second*4)
-				icmping := nettools.NewIcmpPing(addr.String(), time.Second*4)
-				rest := icmping.Ping()
-				if rest.Error() == nil {
-					delay := rest.Result()
-					seelog.Info("[func:IcmpPing] Finish Addr:", addr, delay, "ms")
-					stat.AvgDelay = stat.AvgDelay + rest.Result()
-					if stat.MaxDelay < delay {
-						stat.MaxDelay = delay
-					}
-					if stat.MinDelay == -1 || stat.MinDelay > delay {
-						stat.MinDelay = delay
-					}
-					stat.RevcPk = stat.RevcPk + 1
-
-					stat.SendPk = stat.SendPk + 1
-					stat.LossPk = int((float64(lossPK) / float64(stat.SendPk)) * 100)
-					duringtime := time.Now().UnixNano() - starttime
-					time.Sleep(time.Duration(3000*1000000-duringtime) * time.Nanosecond)
-				} else {
-					seelog.Debug("[func:StartPing IcmpPing] ID:", i, " IP:", addr, "| err:", rest.Error())
-					lossPK = lossPK + 1
-				}
-
-			}
-			if stat.RevcPk > 0 {
-				stat.AvgDelay = stat.AvgDelay / stat.RevcPk
-			} else {
-				stat.AvgDelay = 0.0
-			}
-			seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " MaxDelay:", stat.MaxDelay, " MinDelay:", stat.MinDelay, " AvgDelay:", stat.AvgDelay, " Revc:", stat.RevcPk, " LossPK:", stat.LossPk)
-		} else {
-			stat.AvgDelay = 0.00
-			stat.MinDelay = 0.00
-			stat.MaxDelay = 0.00
-			stat.SendPk = 0
-			stat.RevcPk = 0
-			stat.LossPk = 100
-			seelog.Debug("[func:IcmpPing] Finish Addr:", addr, " Unable to resolve destination host")
-		}
-
-	}
+	r.GET("/config.html", func(context *gin.Context) {
+		context.HTML(http.StatusOK, "config.html", gin.H{
+			"title": "Main website",
+		})
+	})
+	// r.StaticFS("/img", http.Dir("./images"))
 
+	//r.StaticFile("index", "html/*")
+	r.Run()
 }

+ 126 - 0
nettools/dns.go

@@ -0,0 +1,126 @@
+package nettools
+
+import (
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"github.com/miekg/dns"
+	"golang.org/x/net/context"
+	"net"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type DnsPingResult struct {
+	Time int
+	Err  error
+	IP   net.IP
+}
+
+func (dnsR *DnsPingResult) Result() int {
+	return dnsR.Time
+}
+
+func (dnsR *DnsPingResult) Error() error {
+	return dnsR.Err
+}
+
+func (dnsR *DnsPingResult) String() string {
+	if dnsR.Err != nil {
+		return fmt.Sprintf("%s", dnsR.Err)
+	} else {
+		return fmt.Sprintf("%s: time=%d ms", dnsR.IP.String(), dnsR.Time)
+	}
+}
+
+type DnsPing struct {
+	host    string
+	Port    uint16
+	Timeout time.Duration
+
+	// udp, tcp, tcp-tls,默认 udp
+	Net string
+
+	// A, AAAA, NS, ...,默认 NS
+	Type string
+
+	// 查询域名,默认 .
+	Domain string
+
+	// Net 为 tcp-tls 时,是否跳过证书验证
+	Insecure bool
+
+	ip net.IP
+}
+
+func (dnsC *DnsPing) SetHost(host string) {
+	dnsC.host = host
+	dnsC.ip = net.ParseIP(host)
+}
+
+func (dnsC *DnsPing) Host() string {
+	return dnsC.host
+}
+
+func (dnsC *DnsPing) Ping() IPingResult {
+	return dnsC.PingContext(context.Background())
+}
+
+func (dnsC *DnsPing) PingContext(ctx context.Context) IPingResult {
+	ip := cloneIP(dnsC.ip)
+	if ip == nil {
+		var err error
+		ip, err = LookupFunc(dnsC.host)
+		if err != nil {
+			return &DnsPingResult{0, err, nil}
+		}
+	}
+
+	msg := &dns.Msg{}
+	qtype, ok := dns.StringToType[dnsC.Type]
+	if !ok {
+		return &DnsPingResult{0, errors.New("unknown type"), nil}
+	}
+	if !strings.HasSuffix(dnsC.Domain, ".") {
+		dnsC.Domain += "."
+	}
+	msg.SetQuestion(dnsC.Domain, qtype)
+	msg.MsgHdr.RecursionDesired = true
+
+	client := &dns.Client{}
+	client.Net = dnsC.Net
+	client.Timeout = dnsC.Timeout
+	client.TLSConfig = &tls.Config{
+		ServerName:         dnsC.host,
+		InsecureSkipVerify: dnsC.Insecure,
+	}
+
+	t0 := time.Now()
+	r, _, err := client.ExchangeContext(ctx, msg, net.JoinHostPort(ip.String(), strconv.Itoa(int(dnsC.Port))))
+	if err != nil {
+		return &DnsPingResult{0, err, nil}
+	}
+	if r == nil || r.Response == false || r.Opcode != dns.OpcodeQuery {
+		return &DnsPingResult{0, errors.New("response error"), nil}
+	}
+	return &DnsPingResult{int(time.Now().Sub(t0).Milliseconds()), nil, ip}
+}
+
+func NewDnsPing(host string, timeout time.Duration) *DnsPing {
+	return &DnsPing{
+		host:     host,
+		Port:     53,
+		Timeout:  timeout,
+		Net:      "udp",
+		Type:     "NS",
+		Domain:   ".",
+		Insecure: false,
+		ip:       net.ParseIP(host),
+	}
+}
+
+var (
+	_ IPing       = (*DnsPing)(nil)
+	_ IPingResult = (*DnsPingResult)(nil)
+)

+ 151 - 0
nettools/https.go

@@ -0,0 +1,151 @@
+package nettools
+
+import (
+	"crypto/tls"
+	"fmt"
+	"golang.org/x/net/context"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+)
+
+type HttpPingResult struct {
+	Time   int
+	Proto  string
+	Status int
+	Length int
+	Err    error
+	IP     net.IP
+}
+
+func (httpR *HttpPingResult) Result() int {
+	return httpR.Time
+}
+
+func (httpR *HttpPingResult) Error() error {
+	return httpR.Err
+}
+
+func (httpR *HttpPingResult) String() string {
+	if httpR.Err != nil {
+		return fmt.Sprintf("%s", httpR.Err)
+	} else {
+		return fmt.Sprintf("%s: protocol=%s, status=%d, length=%d, time=%d ms", httpR.IP.String(), httpR.Proto, httpR.Status, httpR.Length, httpR.Time)
+	}
+}
+
+type HttpPing struct {
+	Method  string
+	URL     string
+	Timeout time.Duration
+
+	// 以下参数全部为可选
+	DisableHttp2       bool
+	DisableCompression bool
+	Insecure           bool
+	Referrer           string
+	UserAgent          string
+	IP                 net.IP
+}
+
+func (httpC *HttpPing) Ping() IPingResult {
+	return httpC.PingContext(context.Background())
+}
+
+func (httpC *HttpPing) PingContext(ctx context.Context) IPingResult {
+	u, err := url.Parse(httpC.URL)
+	if err != nil {
+		return httpC.errorResult(err)
+	}
+	host := u.Hostname()
+	ip := cloneIP(httpC.IP)
+	if ip == nil {
+		var err error
+		ip, err = LookupFunc(host)
+		if err != nil {
+			return httpC.errorResult(err)
+		}
+	}
+
+	dialer := &net.Dialer{
+		Timeout:   httpC.Timeout,
+		KeepAlive: -1,
+	}
+
+	dialfunc := func(ctx context.Context, network, address string) (net.Conn, error) {
+		h, p, err := net.SplitHostPort(address)
+		if err != nil {
+			return nil, err
+		}
+		if ip == nil || !strings.EqualFold(h, host) {
+			var err error
+			ip, err = LookupFunc(h)
+			if err != nil {
+				return nil, err
+			}
+		}
+		addr := net.JoinHostPort(ip.String(), p)
+		return dialer.DialContext(ctx, network, addr)
+	}
+
+	trans := http.DefaultTransport.(*http.Transport).Clone()
+	trans.DialContext = dialfunc
+	trans.DisableKeepAlives = true
+	trans.MaxIdleConnsPerHost = -1
+	trans.DisableCompression = httpC.DisableCompression
+	trans.ForceAttemptHTTP2 = !httpC.DisableHttp2
+	trans.TLSClientConfig = &tls.Config{
+		InsecureSkipVerify: httpC.Insecure,
+	}
+
+	req, err := http.NewRequestWithContext(ctx, httpC.Method, httpC.URL, nil)
+	if err != nil {
+		return httpC.errorResult(err)
+	}
+	if httpC.UserAgent == "" {
+		httpC.UserAgent = "httping"
+	}
+	req.Header.Set("User-Agent", httpC.UserAgent)
+	if httpC.Referrer != "" {
+		req.Header.Set("Referer", httpC.Referrer)
+	}
+	client := &http.Client{}
+	client.Transport = trans
+	client.Timeout = httpC.Timeout
+	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
+		return http.ErrUseLastResponse
+	}
+	t0 := time.Now()
+	resp, err := client.Do(req)
+	if err != nil {
+		return httpC.errorResult(err)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return httpC.errorResult(err)
+	}
+	return &HttpPingResult{int(time.Now().Sub(t0).Milliseconds()), resp.Proto, resp.StatusCode, len(body), nil, ip}
+}
+
+func (httpC *HttpPing) errorResult(err error) *HttpPingResult {
+	r := &HttpPingResult{}
+	r.Err = err
+	return r
+}
+
+func NewHttpPing(method, url string, timeout time.Duration) *HttpPing {
+	return &HttpPing{
+		Method:  method,
+		URL:     url,
+		Timeout: timeout,
+	}
+}
+
+var (
+	_ IPing       = (*HttpPing)(nil)
+	_ IPingResult = (*HttpPingResult)(nil)
+)

+ 2 - 2
nettools/icmp_unix.go

@@ -4,6 +4,6 @@ import (
 	"context"
 )
 
-func (this *IcmpPing) ping_rootless(ctx context.Context) IPingResult {
-	return this.rawping("udp")
+func (icmpC *IcmpPing) ping_rootless(ctx context.Context) IPingResult {
+	return icmpC.rawping("udp")
 }

+ 41 - 41
nettools/icmpping.go

@@ -22,19 +22,19 @@ type IcmpPingResult struct {
 	TTL  int
 }
 
-func (this *IcmpPingResult) Result() int {
-	return this.Time
+func (icmpR *IcmpPingResult) Result() int {
+	return icmpR.Time
 }
 
-func (this *IcmpPingResult) Error() error {
-	return this.Err
+func (icmpR *IcmpPingResult) Error() error {
+	return icmpR.Err
 }
 
-func (this *IcmpPingResult) String() string {
-	if this.Err != nil {
-		return fmt.Sprintf("%s", this.Err)
+func (icmpR *IcmpPingResult) String() string {
+	if icmpR.Err != nil {
+		return fmt.Sprintf("%s", icmpR.Err)
 	} else {
-		return fmt.Sprintf("%s: time=%d ms, TTL=%d", this.IP.String(), this.Time, this.TTL)
+		return fmt.Sprintf("%s: time=%d ms, TTL=%d", icmpR.IP.String(), icmpR.Time, icmpR.TTL)
 	}
 }
 
@@ -46,13 +46,13 @@ type IcmpPing struct {
 	Privileged bool
 }
 
-func (this *IcmpPing) SetHost(host string) {
-	this.host = host
-	this.ip = net.ParseIP(host)
+func (icmpC *IcmpPing) SetHost(host string) {
+	icmpC.host = host
+	icmpC.ip = net.ParseIP(host)
 }
 
-func (this *IcmpPing) Host() string {
-	return this.host
+func (icmpC *IcmpPing) Host() string {
+	return icmpC.host
 }
 
 func NewIcmpPing(host string, timeout time.Duration) *IcmpPing {
@@ -63,48 +63,48 @@ func NewIcmpPing(host string, timeout time.Duration) *IcmpPing {
 	return p
 }
 
-func (this *IcmpPing) Ping() IPingResult {
-	return this.PingContext(context.Background())
+func (icmpC *IcmpPing) Ping() IPingResult {
+	return icmpC.PingContext(context.Background())
 }
 
-func (this *IcmpPing) PingContext(ctx context.Context) IPingResult {
-	pingfunc := this.ping_rootless
-	if this.Privileged {
-		pingfunc = this.ping_root
+func (icmpC *IcmpPing) PingContext(ctx context.Context) IPingResult {
+	pingfunc := icmpC.ping_rootless
+	if icmpC.Privileged {
+		pingfunc = icmpC.ping_root
 	}
 	return pingfunc(ctx)
 }
 
-func (this *IcmpPing) ping_root(ctx context.Context) IPingResult {
-	return this.rawping("ip")
+func (icmpC *IcmpPing) ping_root(ctx context.Context) IPingResult {
+	return icmpC.rawping("ip")
 }
 
 // https://github.com/sparrc/go-ping/blob/master/ping.go
 
-func (this *IcmpPing) rawping(network string) IPingResult {
+func (icmpC *IcmpPing) rawping(network string) IPingResult {
 	// 解析IP
-	ip, isipv6, err := this.parseip()
+	ip, isipv6, err := icmpC.parseip()
 	if err != nil {
-		return this.errorResult(err)
+		return icmpC.errorResult(err)
 	}
 
 	// 创建连接
-	conn, err := this.getconn(network, ip, isipv6)
+	conn, err := icmpC.getconn(network, ip, isipv6)
 	if err != nil {
-		return this.errorResult(err)
+		return icmpC.errorResult(err)
 	}
 	defer conn.Close()
-	conn.SetDeadline(time.Now().Add(this.Timeout))
+	conn.SetDeadline(time.Now().Add(icmpC.Timeout))
 
 	// 发送
 	r := rand.New(rand.NewSource(time.Now().UnixNano()))
 	sendData := make([]byte, 32)
 	r.Read(sendData)
 	id := os.Getpid() & 0xffff
-	sendMsg := this.getmsg(isipv6, id, 0, sendData)
+	sendMsg := icmpC.getmsg(isipv6, id, 0, sendData)
 	sendMsgBytes, err := sendMsg.Marshal(nil)
 	if err != nil {
-		return this.errorResult(err)
+		return icmpC.errorResult(err)
 	}
 	var dst net.Addr = &net.IPAddr{IP: ip}
 	if network == "udp" {
@@ -142,7 +142,7 @@ func (this *IcmpPing) rawping(network string) IPingResult {
 			}
 		}
 		if err != nil {
-			return this.errorResult(err)
+			return icmpC.errorResult(err)
 		}
 
 		recvAt := time.Now()
@@ -152,9 +152,9 @@ func (this *IcmpPing) rawping(network string) IPingResult {
 		}
 		recvMsg, err := icmp.ParseMessage(recvProto, recvBytes[:recvSize])
 		if err != nil {
-			return this.errorResult(err)
+			return icmpC.errorResult(err)
 		}
-		recvData, recvID, recvType := this.parserecvmsg(isipv6, recvMsg)
+		recvData, recvID, recvType := icmpC.parserecvmsg(isipv6, recvMsg)
 		// 修正数据长度
 		if len(recvData) > len(sendData) {
 			recvData = recvData[len(recvData)-len(sendData):]
@@ -184,19 +184,19 @@ func (this *IcmpPing) rawping(network string) IPingResult {
 			}
 		case 2:
 			// destination unreachable
-			return this.errorResult(errors.New(fmt.Sprintf("%s: destination unreachable", ip.String())))
+			return icmpC.errorResult(errors.New(fmt.Sprintf("%s: destination unreachable", ip.String())))
 		case 3:
 			// time exceeded
-			return this.errorResult(errors.New(fmt.Sprintf("%s: time exceeded", ip.String())))
+			return icmpC.errorResult(errors.New(fmt.Sprintf("%s: time exceeded", ip.String())))
 		}
 	}
 }
 
-func (this *IcmpPing) parseip() (ip net.IP, ipv6 bool, err error) {
+func (icmpC *IcmpPing) parseip() (ip net.IP, ipv6 bool, err error) {
 	err = nil
-	ip = cloneIP(this.ip)
+	ip = cloneIP(icmpC.ip)
 	if ip == nil {
-		ip, err = LookupFunc(this.host)
+		ip, err = LookupFunc(icmpC.host)
 		if err != nil {
 			return
 		}
@@ -211,7 +211,7 @@ func (this *IcmpPing) parseip() (ip net.IP, ipv6 bool, err error) {
 	return
 }
 
-func (this *IcmpPing) getconn(network string, ip net.IP, isipv6 bool) (*icmp.PacketConn, error) {
+func (icmpC *IcmpPing) getconn(network string, ip net.IP, isipv6 bool) (*icmp.PacketConn, error) {
 	ipv4Proto := map[string]string{"ip": "ip4:icmp", "udp": "udp4"}
 	ipv6Proto := map[string]string{"ip": "ip6:ipv6-icmp", "udp": "udp6"}
 	icmpnetwork := ""
@@ -232,7 +232,7 @@ func (this *IcmpPing) getconn(network string, ip net.IP, isipv6 bool) (*icmp.Pac
 	return conn, nil
 }
 
-func (this *IcmpPing) getmsg(isipv6 bool, id, seq int, data []byte) *icmp.Message {
+func (icmpC *IcmpPing) getmsg(isipv6 bool, id, seq int, data []byte) *icmp.Message {
 	var msgtype icmp.Type = ipv4.ICMPTypeEcho
 	if isipv6 {
 		msgtype = ipv6.ICMPTypeEchoRequest
@@ -250,7 +250,7 @@ func (this *IcmpPing) getmsg(isipv6 bool, id, seq int, data []byte) *icmp.Messag
 	return msg
 }
 
-func (this *IcmpPing) parserecvmsg(isipv6 bool, msg *icmp.Message) (data []byte, id, msgtype int) {
+func (icmpC *IcmpPing) parserecvmsg(isipv6 bool, msg *icmp.Message) (data []byte, id, msgtype int) {
 	id = 0
 	data = nil
 	msgtype = 0
@@ -291,7 +291,7 @@ func (this *IcmpPing) parserecvmsg(isipv6 bool, msg *icmp.Message) (data []byte,
 	return
 }
 
-func (this *IcmpPing) errorResult(err error) IPingResult {
+func (icmpC *IcmpPing) errorResult(err error) IPingResult {
 	r := &IcmpPingResult{}
 	r.Err = err
 	return r

+ 0 - 1
nettools/pingI.go

@@ -10,7 +10,6 @@ import (
 type IPingResult interface {
 	Result() int
 	Error() error
-
 	fmt.Stringer
 }
 

+ 20 - 20
nettools/tcpping.go

@@ -14,19 +14,19 @@ type TcpPingResult struct {
 	IP   net.IP
 }
 
-func (this *TcpPingResult) Result() int {
-	return this.Time
+func (tcpR *TcpPingResult) Result() int {
+	return tcpR.Time
 }
 
-func (this *TcpPingResult) Error() error {
-	return this.Err
+func (tcpR *TcpPingResult) Error() error {
+	return tcpR.Err
 }
 
-func (this *TcpPingResult) String() string {
-	if this.Err != nil {
-		return fmt.Sprintf("%s", this.Err)
+func (tcpR *TcpPingResult) String() string {
+	if tcpR.Err != nil {
+		return fmt.Sprintf("%s", tcpR.Err)
 	} else {
-		return fmt.Sprintf("%s: time=%d ms", this.IP.String(), this.Time)
+		return fmt.Sprintf("%s: time=%d ms", tcpR.IP.String(), tcpR.Time)
 	}
 }
 
@@ -38,34 +38,34 @@ type TcpPing struct {
 	ip net.IP
 }
 
-func (this *TcpPing) SetHost(host string) {
-	this.host = host
-	this.ip = net.ParseIP(host)
+func (tcpC *TcpPing) SetHost(host string) {
+	tcpC.host = host
+	tcpC.ip = net.ParseIP(host)
 }
 
-func (this *TcpPing) Host() string {
-	return this.host
+func (tcpC *TcpPing) Host() string {
+	return tcpC.host
 }
 
-func (this *TcpPing) Ping() IPingResult {
-	return this.PingContext(context.Background())
+func (tcpC *TcpPing) Ping() IPingResult {
+	return tcpC.PingContext(context.Background())
 }
 
-func (this *TcpPing) PingContext(ctx context.Context) IPingResult {
-	ip := cloneIP(this.ip)
+func (tcpC *TcpPing) PingContext(ctx context.Context) IPingResult {
+	ip := cloneIP(tcpC.ip)
 	if ip == nil {
 		var err error
-		ip, err = LookupFunc(this.host)
+		ip, err = LookupFunc(tcpC.host)
 		if err != nil {
 			return &TcpPingResult{0, err, nil}
 		}
 	}
 	dialer := &net.Dialer{
-		Timeout:   this.Timeout,
+		Timeout:   tcpC.Timeout,
 		KeepAlive: -1,
 	}
 	t0 := time.Now()
-	conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(this.Port), 10)))
+	conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(tcpC.Port), 10)))
 	if err != nil {
 		return &TcpPingResult{0, err, nil}
 	}

+ 108 - 0
nettools/tls.go

@@ -0,0 +1,108 @@
+package nettools
+
+import (
+	"crypto/tls"
+	"fmt"
+	"golang.org/x/net/context"
+	"net"
+	"strconv"
+	"time"
+)
+
+type TlsPingResult struct {
+	ConnectionTime int
+	HandshakeTime  int
+	TLSVersion     uint16
+	Err            error
+	IP             net.IP
+}
+
+func (tlsR *TlsPingResult) Result() int {
+	return tlsR.ConnectionTime + tlsR.HandshakeTime
+}
+
+func (tlsR *TlsPingResult) Error() error {
+	return tlsR.Err
+}
+
+func (tlsR *TlsPingResult) String() string {
+	if tlsR.Err != nil {
+		return fmt.Sprintf("%s", tlsR.Err)
+	} else {
+		return fmt.Sprintf("%s: protocol=%s, connection=%d ms, handshake=%d ms, time=%d ms", tlsR.IP.String(), tlsVersionToString(tlsR.TLSVersion), tlsR.ConnectionTime, tlsR.HandshakeTime, tlsR.Result())
+	}
+}
+
+type TlsPing struct {
+	Host              string
+	Port              uint16
+	ConnectionTimeout time.Duration
+	HandshakeTimeout  time.Duration
+
+	// 以下为可选参数
+	TlsVersion uint16
+	Insecure   bool
+	IP         net.IP
+}
+
+func (tslC *TlsPing) Ping() IPingResult {
+	return tslC.PingContext(context.Background())
+}
+
+func (tslC *TlsPing) PingContext(ctx context.Context) IPingResult {
+	ip := cloneIP(tslC.IP)
+	if ip == nil {
+		var err error
+		ip, err = LookupFunc(tslC.Host)
+		if err != nil {
+			return tslC.errorResult(err)
+		}
+	}
+
+	dialer := &net.Dialer{
+		Timeout:   tslC.ConnectionTimeout,
+		KeepAlive: -1,
+	}
+	t0 := time.Now()
+	conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(tslC.Port), 10)))
+	if err != nil {
+		return tslC.errorResult(err)
+	}
+	defer conn.Close()
+	t1 := time.Now()
+	config := &tls.Config{
+		ServerName:         tslC.Host,
+		MinVersion:         tslC.TlsVersion,
+		MaxVersion:         tslC.TlsVersion,
+		InsecureSkipVerify: tslC.Insecure,
+	}
+	client := tls.Client(conn, config)
+	client.SetDeadline(time.Now().Add(tslC.HandshakeTimeout))
+	err = client.Handshake()
+	if err != nil {
+		return tslC.errorResult(err)
+	}
+	defer client.Close()
+	t2 := time.Now()
+	return &TlsPingResult{int(t1.Sub(t0).Milliseconds()), int(t2.Sub(t1).Milliseconds()), client.ConnectionState().Version, nil, ip}
+}
+
+func NewTlsPing(host string, port uint16, ct, ht time.Duration) *TlsPing {
+	return &TlsPing{
+		Host:              host,
+		Port:              port,
+		ConnectionTimeout: ct,
+		HandshakeTimeout:  ht,
+	}
+}
+
+func (tslC *TlsPing) errorResult(err error) *TlsPingResult {
+	r := &TlsPingResult{}
+	r.Err = err
+	return r
+}
+
+var (
+	_ IPing       = (*TlsPing)(nil)
+	_ IPingResult = (*TlsPingResult)(nil)
+)