parent
c702388603
commit
02f8a9a370
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
type Int64 struct {
|
||||||
|
v int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Get() int64 {
|
||||||
|
return atomic.LoadInt64(&a.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Set(v int64) {
|
||||||
|
atomic.StoreInt64(&a.v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) CompareAndSwap(o, n int64) bool {
|
||||||
|
return atomic.CompareAndSwapInt64(&a.v, o, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Swap(v int64) int64 {
|
||||||
|
return atomic.SwapInt64(&a.v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Add(v int64) int64 {
|
||||||
|
return atomic.AddInt64(&a.v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Sub(v int64) int64 {
|
||||||
|
return a.Add(-v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Incr() int64 {
|
||||||
|
return a.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int64) Decr() int64 {
|
||||||
|
return a.Add(-1)
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BufferPool implements a pool of bytes.Buffers in the form of a bounded
|
||||||
|
// channel.
|
||||||
|
type BufferPool struct {
|
||||||
|
c chan *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufferPool creates a new BufferPool bounded to the given size.
|
||||||
|
func NewBufferPool(size int) (bp *BufferPool) {
|
||||||
|
return &BufferPool{
|
||||||
|
c: make(chan *bytes.Buffer, size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a Buffer from the BufferPool, or creates a new one if none are
|
||||||
|
// available in the pool.
|
||||||
|
func (bp *BufferPool) Get() (b *bytes.Buffer) {
|
||||||
|
select {
|
||||||
|
case b = <-bp.c:
|
||||||
|
// reuse existing buffer
|
||||||
|
default:
|
||||||
|
// create new buffer
|
||||||
|
b = bytes.NewBuffer([]byte{})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put returns the given Buffer to the BufferPool.
|
||||||
|
func (bp *BufferPool) Put(b *bytes.Buffer) {
|
||||||
|
b.Reset()
|
||||||
|
select {
|
||||||
|
case bp.c <- b:
|
||||||
|
default: // Discard the buffer if the pool is full.
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
hash: b098305412c9c0105d572881e395da369b0acf7738d1e89eb816b1e69f731aca
|
|
||||||
updated: 2018-01-16T10:18:20.068657958+08:00
|
|
||||||
imports:
|
|
||||||
- name: github.com/codeskyblue/gosuv
|
|
||||||
version: 6f18d35abfca2d1b82e522ea868771436341dd8e
|
|
||||||
subpackages:
|
|
||||||
- pushover
|
|
||||||
- name: github.com/codeskyblue/kexec
|
|
||||||
version: 863094f94c7fb7c235764bf8f0f79cccea78c8eb
|
|
||||||
- name: github.com/equinox-io/equinox
|
|
||||||
version: f24972fa72facf59d05c91c848b65eac38815915
|
|
||||||
- name: github.com/franela/goreq
|
|
||||||
version: bcd34c9993f899273c74baaa95e15386cd97b6e7
|
|
||||||
- name: github.com/glycerine/rbuf
|
|
||||||
version: 54320fe9f6f340f8fbe3f34306e3c32589d97dc4
|
|
||||||
- name: github.com/go-yaml/yaml
|
|
||||||
version: d670f9405373e636a5a2765eea47fac0c9bc91a4
|
|
||||||
- name: github.com/goji/httpauth
|
|
||||||
version: 2da839ab0f4df05a6db5eb277995589dadbd4fb9
|
|
||||||
- name: github.com/gorilla/context
|
|
||||||
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
|
|
||||||
- name: github.com/gorilla/mux
|
|
||||||
version: 5bbbb5b2b5729b132181cc7f4aa3b3c973e9a0ed
|
|
||||||
- name: github.com/gorilla/websocket
|
|
||||||
version: 292fd08b2560ad524ee37396253d71570339a821
|
|
||||||
- name: github.com/kennygrant/sanitize
|
|
||||||
version: 2e6820834a1f36c626bf19a253b7d3cc060e9b8b
|
|
||||||
- name: github.com/lunny/dingtalk_webhook
|
|
||||||
version: e3534c89ef969912856dfa39e56b09e58c5f5daf
|
|
||||||
- name: github.com/mitchellh/go-ps
|
|
||||||
version: 4fdf99ab29366514c69ccccddab5dc58b8d84062
|
|
||||||
- name: github.com/qiniu/log
|
|
||||||
version: a304a74568d6982c5b89de1c68ac8fca3add196a
|
|
||||||
- name: github.com/shirou/gopsutil
|
|
||||||
version: 6a368fb7cd1221fa6ea90facc9447c9a2234c255
|
|
||||||
subpackages:
|
|
||||||
- process
|
|
||||||
- name: github.com/shurcooL/httpfs
|
|
||||||
version: 809beceb23714880abc4a382a00c05f89d13b1cc
|
|
||||||
subpackages:
|
|
||||||
- vfsutil
|
|
||||||
- name: github.com/shurcooL/vfsgen
|
|
||||||
version: bb654eaf43db9a91d1d3201dbd8c4b0423a96872
|
|
||||||
- name: github.com/urfave/cli
|
|
||||||
version: 75104e932ac2ddb944a6ea19d9f9f26316ff1145
|
|
||||||
- name: golang.org/x/net
|
|
||||||
version: 5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec
|
|
||||||
subpackages:
|
|
||||||
- html
|
|
||||||
- html/atom
|
|
||||||
testImports:
|
|
||||||
- name: github.com/gopherjs/gopherjs
|
|
||||||
version: 444abdf920945de5d4a977b572bcc6c674d1e4eb
|
|
||||||
subpackages:
|
|
||||||
- js
|
|
||||||
- name: github.com/jtolds/gls
|
|
||||||
version: 77f18212c9c7edc9bd6a33d383a7b545ce62f064
|
|
||||||
- name: github.com/smartystreets/assertions
|
|
||||||
version: 0b37b35ec7434b77e77a4bb29b79677cced992ea
|
|
||||||
subpackages:
|
|
||||||
- internal/go-render/render
|
|
||||||
- internal/oglematchers
|
|
||||||
- name: github.com/smartystreets/goconvey
|
|
||||||
version: e5b2b7c9111590d019a696c7800593f666e1a7f4
|
|
||||||
subpackages:
|
|
||||||
- convey
|
|
||||||
- convey/gotest
|
|
||||||
- convey/reporting
|
|
@ -0,0 +1,152 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/qiniu/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bool struct {
|
||||||
|
c Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bool) Get() bool {
|
||||||
|
return b.c.Get() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bool) toInt64(v bool) int64 {
|
||||||
|
if v {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bool) Set(v bool) {
|
||||||
|
b.c.Set(b.toInt64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bool) CompareAndSwap(o, n bool) bool {
|
||||||
|
return b.c.CompareAndSwap(b.toInt64(o), b.toInt64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bool) Swap(v bool) bool {
|
||||||
|
return b.c.Swap(b.toInt64(v)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufferPool *BufferPool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 4000行日志缓存
|
||||||
|
bufferPool = NewBufferPool(4000)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MergeWriter struct {
|
||||||
|
lines chan *bytes.Buffer
|
||||||
|
writer io.Writer
|
||||||
|
closed Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMergeWriter(writer io.Writer) *MergeWriter {
|
||||||
|
merger := &MergeWriter{
|
||||||
|
lines: make(chan *bytes.Buffer, 1000),
|
||||||
|
writer: writer,
|
||||||
|
}
|
||||||
|
merger.closed.Set(false)
|
||||||
|
merger.drainLines()
|
||||||
|
return merger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergeWriter) Close() {
|
||||||
|
// log.Printf("Close MergeWriter")
|
||||||
|
if m.closed.CompareAndSwap(false, true) {
|
||||||
|
// log.Printf("Close lines chan")
|
||||||
|
close(m.lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergeWriter) WriteStrLine(line string) {
|
||||||
|
if m.closed.Get() {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
buffer := bufferPool.Get()
|
||||||
|
buffer.WriteString(line)
|
||||||
|
m.lines <- buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergeWriter) WriteLine(line *bytes.Buffer) {
|
||||||
|
if m.closed.Get() {
|
||||||
|
// 需要回收Buffer
|
||||||
|
// log.Printf("Write to closed MergeWrite...")
|
||||||
|
bufferPool.Put(line)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
m.lines <- line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MergeWriter) drainLines() {
|
||||||
|
go func() {
|
||||||
|
for line := range m.lines {
|
||||||
|
m.writer.Write(line.Bytes())
|
||||||
|
// 回收
|
||||||
|
bufferPool.Put(line)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的BufferWriter
|
||||||
|
func (m *MergeWriter) NewWriter(index int) io.Writer {
|
||||||
|
writer := &BufferWriter{
|
||||||
|
merge: m,
|
||||||
|
prefix: fmt.Sprintf(" [P%02d] ", index),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配
|
||||||
|
writer.Buffer = bufferPool.Get()
|
||||||
|
writer.Buffer.WriteString(writer.prefix)
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferWriter struct {
|
||||||
|
Buffer *bytes.Buffer
|
||||||
|
prefix string
|
||||||
|
merge *MergeWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BufferWriter) Write(p []byte) (n int, err error) {
|
||||||
|
n = len(p)
|
||||||
|
|
||||||
|
for len(p) > 0 {
|
||||||
|
index := bytes.IndexByte(p, '\n')
|
||||||
|
if index != -1 {
|
||||||
|
// 写完完整的一行
|
||||||
|
_, err = b.Buffer.Write(p[0 : index+1])
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Writer Buffer failed")
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将buffer转移到merge中
|
||||||
|
b.merge.WriteLine(b.Buffer)
|
||||||
|
|
||||||
|
// 分配:写入新数据
|
||||||
|
b.Buffer = bufferPool.Get()
|
||||||
|
b.Buffer.WriteString(time.Now().Format("15:04:05") + b.prefix)
|
||||||
|
p = p[index+1:]
|
||||||
|
} else {
|
||||||
|
// 剩下不足一行,一口气全部写入
|
||||||
|
_, err = b.Buffer.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Writer Buffer failed")
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
@ -1,274 +1,291 @@
|
|||||||
/* index.js */
|
/* index.js */
|
||||||
var W = {};
|
var W = {};
|
||||||
var testPrograms = [{
|
|
||||||
program: {
|
|
||||||
name: "gggg",
|
|
||||||
command: "",
|
|
||||||
dir: "",
|
|
||||||
autoStart: true,
|
|
||||||
},
|
|
||||||
status: "running",
|
|
||||||
}];
|
|
||||||
|
|
||||||
var vm = new Vue({
|
var vm = new Vue({
|
||||||
el: "#app",
|
el: "#app",
|
||||||
data: {
|
data: {
|
||||||
isConnectionAlive: true,
|
isConnectionAlive: true,
|
||||||
log: {
|
log: {
|
||||||
content: '',
|
content: '',
|
||||||
follow: true,
|
follow: true,
|
||||||
line_count: 0,
|
line_count: 0,
|
||||||
},
|
|
||||||
programs: [],
|
|
||||||
edit: {
|
|
||||||
program: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addNewProgram: function() {
|
|
||||||
console.log("Add")
|
|
||||||
var form = $("#formNewProgram");
|
|
||||||
form.submit(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$("#newProgram").modal('hide')
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showEditProgram: function(p) {
|
|
||||||
this.edit.program = Object.assign({}, p); // here require polyfill.min.js
|
|
||||||
$("#programEdit").modal('show');
|
|
||||||
},
|
|
||||||
editProgram: function() {
|
|
||||||
var p = this.edit.program;
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/programs/" + p.name,
|
|
||||||
method: "PUT",
|
|
||||||
data: JSON.stringify(p),
|
|
||||||
})
|
|
||||||
.then(function(ret) {
|
|
||||||
console.log(ret);
|
|
||||||
$("#programEdit").modal('hide');
|
|
||||||
})
|
|
||||||
// console.log(JSON.stringify(p));
|
|
||||||
},
|
|
||||||
updateBreadcrumb: function() {
|
|
||||||
var pathname = decodeURI(location.pathname || "/");
|
|
||||||
var parts = pathname.split('/');
|
|
||||||
this.breadcrumb = [];
|
|
||||||
if (pathname == "/") {
|
|
||||||
return this.breadcrumb;
|
|
||||||
}
|
|
||||||
var i = 2;
|
|
||||||
for (; i <= parts.length; i += 1) {
|
|
||||||
var name = parts[i - 1];
|
|
||||||
var path = parts.slice(0, i).join('/');
|
|
||||||
this.breadcrumb.push({
|
|
||||||
name: name + (i == parts.length ? ' /' : ''),
|
|
||||||
path: path
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return this.breadcrumb;
|
|
||||||
},
|
|
||||||
refresh: function() {
|
|
||||||
// ws.send("Hello")
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/programs",
|
|
||||||
success: function(data) {
|
|
||||||
vm.programs = data;
|
|
||||||
Vue.nextTick(function() {
|
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
reload: function() {
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/reload",
|
|
||||||
method: "POST",
|
|
||||||
success: function(data) {
|
|
||||||
if (data.status == 0) {
|
|
||||||
alert("reload success");
|
|
||||||
} else {
|
|
||||||
alert(data.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
test: function() {
|
|
||||||
console.log("test");
|
|
||||||
},
|
|
||||||
cmdStart: function(name) {
|
|
||||||
console.log(name);
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/programs/" + name + "/start",
|
|
||||||
method: 'post',
|
|
||||||
success: function(data) {
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cmdStop: function(name) {
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/programs/" + name + "/stop",
|
|
||||||
method: 'post',
|
|
||||||
success: function(data) {
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cmdTail: function(name) {
|
|
||||||
var that = this;
|
|
||||||
if (W.wsLog) {
|
|
||||||
W.wsLog.close()
|
|
||||||
}
|
|
||||||
W.wsLog = newWebsocket("/ws/logs/" + name, {
|
|
||||||
onopen: function(evt) {
|
|
||||||
that.log.content = "";
|
|
||||||
that.log.line_count = 0;
|
|
||||||
},
|
},
|
||||||
onmessage: function(evt) {
|
programs: [],
|
||||||
// strip ansi color
|
slaves: [],
|
||||||
// console.log("DT:", evt.data)
|
edit: {
|
||||||
that.log.content += evt.data.replace(/\033\[[0-9;]*m/g, "");
|
program: null,
|
||||||
that.log.line_count = $.trim(that.log.content).split(/\r\n|\r|\n/).length;
|
|
||||||
if (that.log.follow) {
|
|
||||||
var pre = $(".realtime-log")[0];
|
|
||||||
setTimeout(function() {
|
|
||||||
pre.scrollTop = pre.scrollHeight - pre.clientHeight;
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
this.log.follow = true;
|
|
||||||
$("#modalTailf").modal({
|
|
||||||
show: true,
|
|
||||||
keyboard: true,
|
|
||||||
// keyboard: false,
|
|
||||||
// backdrop: 'static',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cmdDelete: function(name) {
|
|
||||||
if (!confirm("Confirm delete \"" + name + "\"")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/programs/" + name,
|
|
||||||
method: 'delete',
|
|
||||||
success: function(data) {
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
canStop: function(status) {
|
|
||||||
switch (status) {
|
|
||||||
case "running":
|
|
||||||
case "retry wait":
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
methods: {
|
||||||
})
|
addNewProgram: function (slave) {
|
||||||
|
$("#newProgram").modal({
|
||||||
|
show: true,
|
||||||
|
backdrop: 'static',
|
||||||
|
}).data("slave", slave);
|
||||||
|
},
|
||||||
|
formNewProgram: function () {
|
||||||
|
var url = "/api/programs",
|
||||||
|
data = $("#formNewProgram").serialize(),
|
||||||
|
name = $("#formNewProgram").find("[name=name]").val(),
|
||||||
|
disablechars = "./\\";
|
||||||
|
if (!name) {
|
||||||
|
alert("\"" + name + "\" is empty ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (disablechars.indexOf(name[0]) != -1) {
|
||||||
|
alert("\"" + name + "\" Can't starts with \".\" \"/\" \"\\\"")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var slave = $("#newProgram").data("slave");
|
||||||
|
if (slave !== undefined && slave !== "") {
|
||||||
|
url = "/distributed/" + slave + url;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
success: function (data) {
|
||||||
|
if (data.status === 0) {
|
||||||
|
$("#newProgram").modal('hide');
|
||||||
|
} else {
|
||||||
|
window.alert(data.error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (err) {
|
||||||
|
alert(err.responseText)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
Vue.filter('fromNow', function(value) {
|
showEditProgram: function (p, slave) {
|
||||||
return moment(value).fromNow();
|
this.edit.program = Object.assign({}, p); // here require polyfill.min.js
|
||||||
})
|
$("#programEdit").data("slave",slave).modal('show');
|
||||||
|
},
|
||||||
|
|
||||||
Vue.filter('formatBytes', function(value) {
|
editProgram: function () {
|
||||||
var bytes = parseFloat(value);
|
var p = this.edit.program;
|
||||||
if (bytes < 0) return "-";
|
var requestUrl = "/api/programs/" + p.name
|
||||||
else if (bytes < 1024) return bytes + " B";
|
var slave = $("#programEdit").data("slave");
|
||||||
else if (bytes < 1048576) return (bytes / 1024).toFixed(0) + " KB";
|
if (slave !== undefined && slave !== "") {
|
||||||
else if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + " MB";
|
requestUrl = "/distributed/" + slave + requestUrl;
|
||||||
else return (bytes / 1073741824).toFixed(1) + " GB";
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: requestUrl,
|
||||||
|
method: "PUT",
|
||||||
|
data: JSON.stringify(p),
|
||||||
|
}).then(function (ret) {
|
||||||
|
$("#programEdit").modal('hide');
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateBreadcrumb: function () {
|
||||||
|
var pathname = decodeURI(location.pathname || "/");
|
||||||
|
var parts = pathname.split('/');
|
||||||
|
this.breadcrumb = [];
|
||||||
|
if (pathname == "/") {
|
||||||
|
return this.breadcrumb;
|
||||||
|
}
|
||||||
|
var i = 2;
|
||||||
|
for (; i <= parts.length; i += 1) {
|
||||||
|
var name = parts[i - 1];
|
||||||
|
var path = parts.slice(0, i).join('/');
|
||||||
|
this.breadcrumb.push({
|
||||||
|
name: name + (i == parts.length ? ' /' : ''),
|
||||||
|
path: path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return this.breadcrumb;
|
||||||
|
},
|
||||||
|
refresh: function () {
|
||||||
|
// ws.send("Hello")
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/programs",
|
||||||
|
success: function (data) {
|
||||||
|
vm.programs = data;
|
||||||
|
Vue.nextTick(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/distributed/api/programs",
|
||||||
|
success: function (data) {
|
||||||
|
vm.slaves = data;
|
||||||
|
Vue.nextTick(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/*reload: function () {
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/reload",
|
||||||
|
method: "POST",
|
||||||
|
success: function (data) {
|
||||||
|
if (data.status == 0) {
|
||||||
|
alert("reload success");
|
||||||
|
} else {
|
||||||
|
alert(data.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
test: function () {
|
||||||
|
console.log("test");
|
||||||
|
},*/
|
||||||
|
cmdStart: function (name, slave) {
|
||||||
|
console.log(name, slave);
|
||||||
|
requestUrl = "/api/programs/" + name + "/start";
|
||||||
|
if (slave !== undefined && "" !== slave) {
|
||||||
|
requestUrl = "/distributed/" + slave + requestUrl;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: requestUrl,
|
||||||
|
method: 'post',
|
||||||
|
success: function (data) {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cmdStop: function (name, slave) {
|
||||||
|
requestUrl = "/api/programs/" + name + "/stop";
|
||||||
|
if (slave !== undefined && "" !== slave) {
|
||||||
|
requestUrl = "/distributed/" + slave + requestUrl;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: requestUrl,
|
||||||
|
method: 'post',
|
||||||
|
success: function (data) {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cmdTail: function (name, slave) {
|
||||||
|
requestUrl = "/ws/logs/" + name;
|
||||||
|
if (slave !== undefined && "" !== slave) {
|
||||||
|
requestUrl = "/distributed/" + slave + requestUrl;
|
||||||
|
}
|
||||||
|
var that = this;
|
||||||
|
if (W.wsLog) {
|
||||||
|
W.wsLog.close()
|
||||||
|
}
|
||||||
|
W.wsLog = newWebsocket(requestUrl, {
|
||||||
|
onopen: function (evt) {
|
||||||
|
that.log.content = "";
|
||||||
|
that.log.line_count = 0;
|
||||||
|
},
|
||||||
|
onmessage: function (evt) {
|
||||||
|
that.log.content += evt.data.replace(/\033\[[0-9;]*m/g, "");
|
||||||
|
that.log.line_count = $.trim(that.log.content).split(/\r\n|\r|\n/).length;
|
||||||
|
if (that.log.follow) {
|
||||||
|
var pre = $(".realtime-log")[0];
|
||||||
|
setTimeout(function () {
|
||||||
|
pre.scrollTop = pre.scrollHeight - pre.clientHeight;
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.log.follow = true;
|
||||||
|
$("#modalTailf").modal({
|
||||||
|
show: true,
|
||||||
|
keyboard: true,
|
||||||
|
}).on("hide.bs.modal", function (e) {
|
||||||
|
W.wsLog.close();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cmdDelete: function (name, slave) {
|
||||||
|
if (!confirm("Confirm delete \"" + name + "\"")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestUrl = "/api/programs/" + name;
|
||||||
|
if (slave !== undefined && "" !== slave) {
|
||||||
|
requestUrl = "/distributed/" + slave + requestUrl
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: requestUrl,
|
||||||
|
method: 'delete',
|
||||||
|
success: function (data) {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
canStop: function (status) {
|
||||||
|
switch (status) {
|
||||||
|
case "running":
|
||||||
|
case "retry wait":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.filter('colorStatus', function(value) {
|
Vue.filter('fromNow', function (value) {
|
||||||
var makeColorText = function(text, color) {
|
return moment(value).fromNow();
|
||||||
return "<span class='status' style='background-color:" + color + "'>" + text + "</span>";
|
|
||||||
}
|
|
||||||
switch (value) {
|
|
||||||
case "stopping":
|
|
||||||
return makeColorText(value, "#996633");
|
|
||||||
case "running":
|
|
||||||
return makeColorText(value, "green");
|
|
||||||
case "fatal":
|
|
||||||
return makeColorText(value, "red");
|
|
||||||
default:
|
|
||||||
return makeColorText(value, "gray");
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.directive('disable', function(value) {
|
Vue.filter('formatBytes', function (value) {
|
||||||
this.el.disabled = !!value
|
var bytes = parseFloat(value);
|
||||||
|
if (bytes < 0) return "-";
|
||||||
|
else if (bytes < 1024) return bytes + " B";
|
||||||
|
else if (bytes < 1048576) return (bytes / 1024).toFixed(0) + " KB";
|
||||||
|
else if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + " MB";
|
||||||
|
else return (bytes / 1073741824).toFixed(1) + " GB";
|
||||||
})
|
})
|
||||||
|
|
||||||
$(function() {
|
Vue.filter('colorStatus', function (value) {
|
||||||
vm.refresh();
|
var makeColorText = function (text, color) {
|
||||||
|
return "<span class='status' style='background-color:" + color + "'>" + text + "</span>";
|
||||||
|
};
|
||||||
|
switch (value) {
|
||||||
|
case "stopping":
|
||||||
|
return makeColorText(value, "#996633");
|
||||||
|
case "running":
|
||||||
|
return makeColorText(value, "green");
|
||||||
|
case "fatal":
|
||||||
|
return makeColorText(value, "red");
|
||||||
|
default:
|
||||||
|
return makeColorText(value, "gray");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#formNewProgram").submit(function(e) {
|
Vue.directive('disable', function (value) {
|
||||||
var url = "/api/programs",
|
this.el.disabled = !!value
|
||||||
data = $(this).serialize(),
|
});
|
||||||
name = $(this).find("[name=name]").val(),
|
|
||||||
disablechars = "./\\";
|
|
||||||
|
|
||||||
if (!name) {
|
$(function () {
|
||||||
alert("\"" + name + "\" is empty ")
|
vm.refresh();
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (disablechars.indexOf(name[0]) != -1) {
|
|
||||||
alert("\"" + name + "\" Can't starts with \".\" \"/\" \"\\\"")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: url,
|
|
||||||
data: data,
|
|
||||||
success: function(data) {
|
|
||||||
if (data.status === 0) {
|
|
||||||
$("#newProgram").modal('hide');
|
|
||||||
} else {
|
|
||||||
window.alert(data.error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(err) {
|
|
||||||
console.log(err.responseText);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
e.preventDefault()
|
|
||||||
});
|
|
||||||
|
|
||||||
function newEventWatcher() {
|
function newEventWatcher() {
|
||||||
W.events = newWebsocket("/ws/events", {
|
W.events = newWebsocket("/ws/events", {
|
||||||
onopen: function(evt) {
|
onopen: function (evt) {
|
||||||
vm.isConnectionAlive = true;
|
vm.isConnectionAlive = true;
|
||||||
},
|
},
|
||||||
onmessage: function(evt) {
|
onmessage: function (evt) {
|
||||||
console.log("response:" + evt.data);
|
console.log("response:" + evt.data);
|
||||||
vm.refresh();
|
vm.refresh();
|
||||||
},
|
},
|
||||||
onclose: function(evt) {
|
onclose: function (evt) {
|
||||||
W.events = null;
|
W.events = null;
|
||||||
vm.isConnectionAlive = false;
|
vm.isConnectionAlive = false;
|
||||||
console.log("Reconnect after 3s")
|
console.log("Reconnect after 3s");
|
||||||
setTimeout(newEventWatcher, 3000)
|
setTimeout(newEventWatcher, 3000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
newEventWatcher();
|
newEventWatcher();
|
||||||
|
|
||||||
// cancel follow log if people want to see the original data
|
// cancel follow log if people want to see the original data
|
||||||
$(".realtime-log").bind('mousewheel', function(evt) {
|
$(".realtime-log").bind('mousewheel', function (evt) {
|
||||||
if (evt.originalEvent.wheelDelta >= 0) {
|
if (evt.originalEvent.wheelDelta >= 0) {
|
||||||
vm.log.follow = false;
|
vm.log.follow = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
$('#modalTailf').on('hidden.bs.modal', function() {
|
$('#modalTailf').on('hidden.bs.modal', function () {
|
||||||
// do something…
|
// do something…
|
||||||
console.log("Hiddeen")
|
console.log("Hiddeen");
|
||||||
if (W.wsLog) {
|
if (W.wsLog) {
|
||||||
console.log("wsLog closed")
|
console.log("wsLog closed");
|
||||||
W.wsLog.close()
|
W.wsLog.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
@ -1,48 +1,52 @@
|
|||||||
/* javascript */
|
/* javascript */
|
||||||
var vm = new Vue({
|
var vm = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
name: name,
|
name: name,
|
||||||
pid: '-',
|
pid: '-',
|
||||||
childPids: [],
|
childPids: [],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var maxDataCount = 30;
|
var maxDataCount = 30;
|
||||||
var ws = newWebsocket('/ws/perfs/' + name, {
|
var requstUrl = "/ws/perfs/" + name;
|
||||||
onopen: function(evt) {
|
if ("" !== slave) {
|
||||||
console.log(evt);
|
requstUrl = "/distributed/" + slave + requstUrl;
|
||||||
},
|
}
|
||||||
onmessage: function(evt) {
|
var ws = newWebsocket(requstUrl, {
|
||||||
var data = JSON.parse(evt.data);
|
onopen: function(evt) {
|
||||||
vm.pid = data.pid;
|
console.log(evt);
|
||||||
vm.childPids = data.pids;
|
},
|
||||||
console.log("pid", data.pid, data); //evt.data.pid);
|
onmessage: function(evt) {
|
||||||
if (memData && data.rss) {
|
var data = JSON.parse(evt.data);
|
||||||
memData.push({
|
vm.pid = data.pid;
|
||||||
value: [new Date(), data.rss],
|
vm.childPids = data.pids;
|
||||||
})
|
console.log("pid", data.pid, data); //evt.data.pid);
|
||||||
if (memData.length > maxDataCount) {
|
if (memData && data.rss) {
|
||||||
memData.shift();
|
memData.push({
|
||||||
}
|
value: [new Date(), data.rss],
|
||||||
chartMem.setOption({
|
})
|
||||||
series: [{
|
if (memData.length > maxDataCount) {
|
||||||
data: memData,
|
memData.shift();
|
||||||
}]
|
}
|
||||||
});
|
chartMem.setOption({
|
||||||
}
|
series: [{
|
||||||
if (cpuData && data.pcpu !== undefined) {
|
data: memData,
|
||||||
cpuData.push({
|
}]
|
||||||
value: [new Date(), data.pcpu],
|
});
|
||||||
})
|
}
|
||||||
if (cpuData.length > maxDataCount) {
|
if (cpuData && data.pcpu !== undefined) {
|
||||||
cpuData.shift();
|
cpuData.push({
|
||||||
}
|
value: [new Date(), data.pcpu],
|
||||||
chartCpu.setOption({
|
})
|
||||||
series: [{
|
if (cpuData.length > maxDataCount) {
|
||||||
data: cpuData,
|
cpuData.shift();
|
||||||
}]
|
}
|
||||||
})
|
chartCpu.setOption({
|
||||||
|
series: [{
|
||||||
|
data: cpuData,
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
Loading…
Reference in new issue