mirror of https://github.com/shunfei/cronsun
parent
75b49bddae
commit
7c61222b68
|
@ -31,13 +31,20 @@ var UpgradeCmd = &cobra.Command{
|
||||||
ea.Exit("invalid version number")
|
ea.Exit("invalid version number")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodesById := getIPMapper(ea)
|
||||||
if prever < "0.3.0" {
|
if prever < "0.3.0" {
|
||||||
fmt.Println("upgrading data to version 0.3.0")
|
fmt.Println("upgrading data to version 0.3.0")
|
||||||
nodesById := getIPMapper(ea)
|
|
||||||
if to_0_3_0(ea, nodesById) {
|
if to_0_3_0(ea, nodesById) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prever < "0.3.1" {
|
||||||
|
fmt.Println("upgrading data to version 0.3.1")
|
||||||
|
if to_0_3_1(ea, nodesById) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +139,8 @@ func to_0_3_0(ea *ExitAction, nodesById map[string]*cronsun.Node) (shouldStop bo
|
||||||
cronsun.GetDb().WithC(cronsun.Coll_JobLog, func(c *mgo.Collection) error {
|
cronsun.GetDb().WithC(cronsun.Coll_JobLog, func(c *mgo.Collection) error {
|
||||||
for ip, node := range nodesById {
|
for ip, node := range nodesById {
|
||||||
_, err = c.UpdateAll(bson.M{"node": ip}, bson.M{"$set": bson.M{"node": node.ID, "hostname": node.Hostname}})
|
_, err = c.UpdateAll(bson.M{"node": ip}, bson.M{"$set": bson.M{"node": node.ID, "hostname": node.Hostname}})
|
||||||
if err != nil {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("failed to upgrade job logs: ", err.Error())
|
fmt.Println("failed to upgrade job logs: ", err.Error())
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,10 +152,39 @@ func to_0_3_0(ea *ExitAction, nodesById map[string]*cronsun.Node) (shouldStop bo
|
||||||
cronsun.GetDb().WithC(cronsun.Coll_JobLatestLog, func(c *mgo.Collection) error {
|
cronsun.GetDb().WithC(cronsun.Coll_JobLatestLog, func(c *mgo.Collection) error {
|
||||||
for ip, node := range nodesById {
|
for ip, node := range nodesById {
|
||||||
_, err = c.UpdateAll(bson.M{"node": ip}, bson.M{"$set": bson.M{"node": node.ID, "hostname": node.Hostname}})
|
_, err = c.UpdateAll(bson.M{"node": ip}, bson.M{"$set": bson.M{"node": node.ID, "hostname": node.Hostname}})
|
||||||
if err != nil {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("failed to upgrade job latest logs: ", err.Error())
|
fmt.Println("failed to upgrade job latest logs: ", err.Error())
|
||||||
}
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldStop = true
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// to_0_3_0 can be run many times
|
||||||
|
func to_0_3_1(ea *ExitAction, nodesById map[string]*cronsun.Node) (shouldStop bool) {
|
||||||
|
// upgrade logs
|
||||||
|
var err error
|
||||||
|
cronsun.GetDb().WithC(cronsun.Coll_JobLog, func(c *mgo.Collection) error {
|
||||||
|
for _, node := range nodesById {
|
||||||
|
_, err = c.UpdateAll(bson.M{"node": node.ID}, bson.M{"$set": bson.M{"ip": node.IP}})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to upgrade job logs: ", err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldStop = true
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
cronsun.GetDb().WithC(cronsun.Coll_JobLatestLog, func(c *mgo.Collection) error {
|
||||||
|
for _, node := range nodesById {
|
||||||
|
_, err = c.UpdateAll(bson.M{"node": node.ID}, bson.M{"$set": bson.M{"ip": node.IP}})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to upgrade job latest logs: ", err.Error())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
job.go
5
job.go
|
@ -69,6 +69,7 @@ type Job struct {
|
||||||
// 执行任务的结点,用于记录 job log
|
// 执行任务的结点,用于记录 job log
|
||||||
runOn string
|
runOn string
|
||||||
hostname string
|
hostname string
|
||||||
|
ip string
|
||||||
// 用于存储分隔后的任务
|
// 用于存储分隔后的任务
|
||||||
cmd []string
|
cmd []string
|
||||||
// 控制同时执行任务数
|
// 控制同时执行任务数
|
||||||
|
@ -186,9 +187,9 @@ func (j *Job) unlimit() {
|
||||||
atomic.AddInt64(j.Count, -1)
|
atomic.AddInt64(j.Count, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Job) Init(nodeID, hostname string) {
|
func (j *Job) Init(nodeID, hostname, ip string) {
|
||||||
var c int64
|
var c int64
|
||||||
j.Count, j.runOn, j.hostname = &c, nodeID, hostname
|
j.Count, j.runOn, j.hostname, j.ip = &c, nodeID, hostname, ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cmd) lockTtl() int64 {
|
func (c *Cmd) lockTtl() int64 {
|
||||||
|
|
|
@ -25,6 +25,7 @@ type JobLog struct {
|
||||||
Name string `bson:"name" json:"name"` // 任务名称
|
Name string `bson:"name" json:"name"` // 任务名称
|
||||||
Node string `bson:"node" json:"node"` // 运行此次任务的节点 id,索引
|
Node string `bson:"node" json:"node"` // 运行此次任务的节点 id,索引
|
||||||
Hostname string `bson:"hostname" json:"hostname"` // 运行此次任务的节点主机名称,索引
|
Hostname string `bson:"hostname" json:"hostname"` // 运行此次任务的节点主机名称,索引
|
||||||
|
IP string `bson:"ip" json:"ip"` // 运行此次任务的节点主机IP,索引
|
||||||
Command string `bson:"command" json:"command,omitempty"` // 执行的命令,包括参数
|
Command string `bson:"command" json:"command,omitempty"` // 执行的命令,包括参数
|
||||||
Output string `bson:"output" json:"output,omitempty"` // 任务输出的所有内容
|
Output string `bson:"output" json:"output,omitempty"` // 任务输出的所有内容
|
||||||
Success bool `bson:"success" json:"success"` // 是否执行成功
|
Success bool `bson:"success" json:"success"` // 是否执行成功
|
||||||
|
@ -98,6 +99,7 @@ func CreateJobLog(j *Job, t time.Time, rs string, success bool) {
|
||||||
|
|
||||||
Node: j.runOn,
|
Node: j.runOn,
|
||||||
Hostname: j.hostname,
|
Hostname: j.hostname,
|
||||||
|
IP: j.ip,
|
||||||
|
|
||||||
Command: j.Command,
|
Command: j.Command,
|
||||||
Output: rs,
|
Output: rs,
|
||||||
|
@ -126,7 +128,7 @@ func CreateJobLog(j *Job, t time.Time, rs string, success bool) {
|
||||||
JobLog: jl,
|
JobLog: jl,
|
||||||
}
|
}
|
||||||
latestLog.Id = ""
|
latestLog.Id = ""
|
||||||
if err := mgoDB.Upsert(Coll_JobLatestLog, bson.M{"node": jl.Node, "hostname": jl.Hostname, "jobId": jl.JobId, "jobGroup": jl.JobGroup}, latestLog); err != nil {
|
if err := mgoDB.Upsert(Coll_JobLatestLog, bson.M{"node": jl.Node, "hostname": jl.Hostname, "ip": jl.IP, "jobId": jl.JobId, "jobGroup": jl.JobGroup}, latestLog); err != nil {
|
||||||
log.Errorf(err.Error())
|
log.Errorf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ func (n *Node) loadJobs() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
job.Init(n.ID, n.Hostname)
|
job.Init(n.ID, n.Hostname, n.IP)
|
||||||
n.addJob(job, false)
|
n.addJob(job, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ func (n *Node) groupAddNode(g *cronsun.Group) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
job.Init(n.ID, n.Hostname)
|
job.Init(n.ID, n.Hostname, n.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmds := job.Cmds(n.ID, n.groups)
|
cmds := job.Cmds(n.ID, n.groups)
|
||||||
|
@ -394,7 +394,7 @@ func (n *Node) watchJobs() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
job.Init(n.ID, n.Hostname)
|
job.Init(n.ID, n.Hostname, n.IP)
|
||||||
n.addJob(job, true)
|
n.addJob(job, true)
|
||||||
case ev.IsModify():
|
case ev.IsModify():
|
||||||
job, err := cronsun.GetJobFromKv(ev.Kv.Key, ev.Kv.Value)
|
job, err := cronsun.GetJobFromKv(ev.Kv.Key, ev.Kv.Value)
|
||||||
|
@ -403,7 +403,7 @@ func (n *Node) watchJobs() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
job.Init(n.ID, n.Hostname)
|
job.Init(n.ID, n.Hostname, n.IP)
|
||||||
n.modJob(job)
|
n.modJob(job)
|
||||||
case ev.Type == client.EventTypeDelete:
|
case ev.Type == client.EventTypeDelete:
|
||||||
n.delJob(cronsun.GetIDFromKey(string(ev.Kv.Key)))
|
n.delJob(cronsun.GetIDFromKey(string(ev.Kv.Key)))
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VersionNumber = "0.3.0"
|
const VersionNumber = "0.3.1"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = fmt.Sprintf("v%s (build %s)", VersionNumber, runtime.Version())
|
Version = fmt.Sprintf("v%s (build %s)", VersionNumber, runtime.Version())
|
||||||
|
|
|
@ -15,9 +15,17 @@ import (
|
||||||
|
|
||||||
func EnsureJobLogIndex() {
|
func EnsureJobLogIndex() {
|
||||||
cronsun.GetDb().WithC(cronsun.Coll_JobLog, func(c *mgo.Collection) error {
|
cronsun.GetDb().WithC(cronsun.Coll_JobLog, func(c *mgo.Collection) error {
|
||||||
return c.EnsureIndex(mgo.Index{
|
c.EnsureIndex(mgo.Index{
|
||||||
Key: []string{"beginTime"},
|
Key: []string{"beginTime"},
|
||||||
})
|
})
|
||||||
|
c.EnsureIndex(mgo.Index{
|
||||||
|
Key: []string{"hostname"},
|
||||||
|
})
|
||||||
|
c.EnsureIndex(mgo.Index{
|
||||||
|
Key: []string{"ip"},
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +58,21 @@ func (jl *JobLog) GetDetail(ctx *Context) {
|
||||||
outJSON(ctx.W, logDetail)
|
outJSON(ctx.W, logDetail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchText(field string, keywords []string) (q []bson.M) {
|
||||||
|
for _, k := range keywords {
|
||||||
|
k = strings.TrimSpace(k)
|
||||||
|
if len(k) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q = append(q, bson.M{field: bson.M{"$regex": bson.RegEx{Pattern: k, Options: "i"}}})
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
func (jl *JobLog) GetList(ctx *Context) {
|
func (jl *JobLog) GetList(ctx *Context) {
|
||||||
hostnames := getStringArrayFromQuery("hostnames", ",", ctx.R)
|
hostnames := getStringArrayFromQuery("hostnames", ",", ctx.R)
|
||||||
|
ips := getStringArrayFromQuery("ips", ",", ctx.R)
|
||||||
names := getStringArrayFromQuery("names", ",", ctx.R)
|
names := getStringArrayFromQuery("names", ",", ctx.R)
|
||||||
ids := getStringArrayFromQuery("ids", ",", ctx.R)
|
ids := getStringArrayFromQuery("ids", ",", ctx.R)
|
||||||
begin := getTime(ctx.R.FormValue("begin"))
|
begin := getTime(ctx.R.FormValue("begin"))
|
||||||
|
@ -62,26 +83,18 @@ func (jl *JobLog) GetList(ctx *Context) {
|
||||||
orderBy := "-beginTime"
|
orderBy := "-beginTime"
|
||||||
|
|
||||||
query := bson.M{}
|
query := bson.M{}
|
||||||
if len(hostnames) > 0 {
|
var textSearch = make([]bson.M, 0, 2)
|
||||||
query["hostname"] = bson.M{"$in": hostnames}
|
textSearch = append(textSearch, searchText("hostname", hostnames)...)
|
||||||
|
textSearch = append(textSearch, searchText("name", names)...)
|
||||||
|
|
||||||
|
if len(ips) > 0 {
|
||||||
|
query["ip"] = bson.M{"$in": ips}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
query["jobId"] = bson.M{"$in": ids}
|
query["jobId"] = bson.M{"$in": ids}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(names) > 0 {
|
|
||||||
var search []bson.M
|
|
||||||
for _, k := range names {
|
|
||||||
k = strings.TrimSpace(k)
|
|
||||||
if len(k) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
search = append(search, bson.M{"name": bson.M{"$regex": bson.RegEx{Pattern: k, Options: "i"}}})
|
|
||||||
}
|
|
||||||
query["$or"] = search
|
|
||||||
}
|
|
||||||
|
|
||||||
if !begin.IsZero() {
|
if !begin.IsZero() {
|
||||||
query["beginTime"] = bson.M{"$gte": begin}
|
query["beginTime"] = bson.M{"$gte": begin}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +106,10 @@ func (jl *JobLog) GetList(ctx *Context) {
|
||||||
query["success"] = false
|
query["success"] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(textSearch) > 0 {
|
||||||
|
query["$or"] = textSearch
|
||||||
|
}
|
||||||
|
|
||||||
var pager struct {
|
var pager struct {
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
List []*cronsun.JobLog `json:"list"`
|
List []*cronsun.JobLog `json:"list"`
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -61,7 +61,7 @@
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div id="initloader"></div>
|
<div id="initloader"></div>
|
||||||
</div>
|
</div>
|
||||||
<script src="build.js?v=36c2198"></script>
|
<script src="build.js?v=75b49bd"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
<style scoped>
|
||||||
|
.ui.vertical.menu h4 {
|
||||||
|
background: rgba(0,0,0,.05);
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="ui blue inverted menu fixed">
|
<div class="ui blue inverted menu fixed">
|
||||||
|
@ -7,25 +15,35 @@
|
||||||
<router-link v-if="shouldOpen" class="item" to="/job" v-bind:class="{active: this.$route.path.indexOf('/job') === 0}"><i class="calendar icon"></i> {{$L('job')}}</router-link>
|
<router-link v-if="shouldOpen" class="item" to="/job" v-bind:class="{active: this.$route.path.indexOf('/job') === 0}"><i class="calendar icon"></i> {{$L('job')}}</router-link>
|
||||||
<router-link v-if="shouldOpen" class="item" to="/node" v-bind:class="{active: this.$route.path.indexOf('/node') === 0}"><i class="server icon"></i> {{$L('node')}}</router-link>
|
<router-link v-if="shouldOpen" class="item" to="/node" v-bind:class="{active: this.$route.path.indexOf('/node') === 0}"><i class="server icon"></i> {{$L('node')}}</router-link>
|
||||||
<router-link v-if="$store.getters.enabledAuth && $store.getters.role === 1" class="item" to="/admin/account/list" v-bind:class="{active: this.$route.path.indexOf('/admin/account') === 0}"><i class="user icon"></i> {{$L('account')}}</router-link>
|
<router-link v-if="$store.getters.enabledAuth && $store.getters.role === 1" class="item" to="/admin/account/list" v-bind:class="{active: this.$route.path.indexOf('/admin/account') === 0}"><i class="user icon"></i> {{$L('account')}}</router-link>
|
||||||
|
<a class="item" href="https://github.com/shunfei/cronsun/wiki" target="_blank"><i class="external alternate icon"></i> Docs</a>
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
<router-link to="/user/setpwd" class="item" v-if="this.$store.getters.email"><i class="user icon"></i> {{this.$store.getters.email}}</router-link>
|
<router-link to="/user/setpwd" class="item" v-if="this.$store.getters.email"><i class="user icon"></i> {{this.$store.getters.email}}</router-link>
|
||||||
<a class="item" v-if="this.$store.getters.email" href="#" v-on:click="logout"><i class="sign out icon"></i></a>
|
<a class="item" v-if="this.$store.getters.email" href="#" v-on:click="logout"><i class="sign out icon"></i></a>
|
||||||
|
|
||||||
<div ref="langSelection" class="ui right icon dropdown item">
|
<a class="item" href="#" @click.prevent="toggleSetting"><i class="settings icon"></i></a>
|
||||||
<i class="world icon" style="margin-left:-1px; margin-right: 8px;"></i>
|
|
||||||
<span class="text">Language</span>
|
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
<div class="menu">
|
|
||||||
<div class="item" v-for="lang in $Lang.supported" :data-value="lang.code">{{lang.name}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 55px;"></div>
|
<div style="height: 55px;"></div>
|
||||||
<div class="ui container">
|
<div class="ui container pusher">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ref="sidebar" class="ui sidebar right vertical menu">
|
||||||
|
<h4 class="item">{{$store.getters.version}}</h4>
|
||||||
|
<h4 class="item">Language</h4>
|
||||||
|
<div class="item">
|
||||||
|
<div class="menu">
|
||||||
|
<a class="item" :class="{active: locale === lang.code}" href="#" v-for="lang in $Lang.supported" @click.prevent="selectLanguage(lang.code)">{{lang.name}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4 class="item">{{$L('node show as')}}</h4>
|
||||||
|
<div class="item">
|
||||||
|
<div class="menu">
|
||||||
|
<a class="item" :class="{active: hostshow === h.value}" href="#" v-for="h in hostshowsList" @click.prevent="selectHostshows(h.value)">{{h.name}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Messager/>
|
<Messager/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -39,21 +57,25 @@ export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
store,
|
store,
|
||||||
|
|
||||||
mounted: function(){
|
data: function() {
|
||||||
$(this.$refs.langSelection).dropdown({
|
return {
|
||||||
onChange: function(value, text){
|
hostshowsList: [{name: this.$L('hostname'), value: 'hostname'}, {name: 'IP', value: 'ip'}],
|
||||||
var old = window.$.cookie('locale');
|
locale: ''
|
||||||
if (old !== value) {
|
|
||||||
window.$.cookie('locale', value)
|
|
||||||
window.location.reload()
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
|
||||||
|
created: function() {
|
||||||
|
this.locale = window.$.cookie('locale');
|
||||||
|
this.$store.commit('setShowWithHostname', window.$.cookie('hostshows') === 'hostname');
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
shouldOpen() {
|
shouldOpen() {
|
||||||
return !this.$store.getters.enabledAuth || (this.$store.getters.enabledAuth && this.$store.getters.email)
|
return !this.$store.getters.enabledAuth || (this.$store.getters.enabledAuth && this.$store.getters.email)
|
||||||
|
},
|
||||||
|
|
||||||
|
hostshow() {
|
||||||
|
return this.$store.getters.showWithHostname ? 'hostname' : 'ip';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -67,6 +89,28 @@ export default {
|
||||||
vm.$router.push('/login');
|
vm.$router.push('/login');
|
||||||
}).
|
}).
|
||||||
do();
|
do();
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleSetting() {
|
||||||
|
$(this.$refs.sidebar).sidebar('toggle');
|
||||||
|
},
|
||||||
|
|
||||||
|
selectLanguage(code) {
|
||||||
|
if (this.locale !== code) {
|
||||||
|
window.$.cookie('locale', code);
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.toggleSetting();
|
||||||
|
},
|
||||||
|
|
||||||
|
selectHostshows(v) {
|
||||||
|
if (this.hostshow !== v) {
|
||||||
|
window.$.cookie('hostshows', v);
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.toggleSetting();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(){},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
show(jobName, jobGroup, jobId){
|
show(jobName, jobGroup, jobId){
|
||||||
this.jobName = jobName;
|
this.jobName = jobName;
|
||||||
|
@ -53,7 +51,7 @@ export default {
|
||||||
onsucceed(200, (resp)=>{
|
onsucceed(200, (resp)=>{
|
||||||
var nodes = [{value: 'all nodes', name: vm.$L('all nodes')}];
|
var nodes = [{value: 'all nodes', name: vm.$L('all nodes')}];
|
||||||
for (var i in resp) {
|
for (var i in resp) {
|
||||||
nodes.push({value: resp[i], name: vm.$store.getters.getHostnameByID(resp[i])})
|
nodes.push({value: resp[i], name: vm.$store.getters.hostshows(resp[i])})
|
||||||
}
|
}
|
||||||
vm.nodes = nodes;
|
vm.nodes = nodes;
|
||||||
}).
|
}).
|
||||||
|
|
|
@ -183,7 +183,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
formatLatest: function(latest){
|
formatLatest: function(latest){
|
||||||
return this.$L('on {node} took {times}, {begin ~ end}', latest.hostname, formatDuration(latest.beginTime, latest.endTime), formatTime(latest.beginTime, latest.endTime));
|
return this.$L('on {node} took {times}, {begin ~ end}', this.$store.getters.hostshowsWithoutTip(latest.node), formatDuration(latest.beginTime, latest.endTime), formatTime(latest.beginTime, latest.endTime));
|
||||||
},
|
},
|
||||||
|
|
||||||
showExecuteJobModal: function(jobName, jobGroup, jobId){
|
showExecuteJobModal: function(jobName, jobGroup, jobId){
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{$L('select nodes')}}</label>
|
<label>{{$L('select nodes')}}</label>
|
||||||
<Dropdown :title="$L('select nodes')" v-bind:items="prefetchs.nodes" v-on:change="changeNodes" :selected="nodes" :multiple="true"/>
|
<Dropdown :title="$L('select nodes')" v-bind:items="$store.getters.dropdownNodes" v-on:change="changeNodes" :selected="nodes" :multiple="true"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<button class="fluid ui button" type="button" v-on:click="submit">{{$L('submit query')}}</button>
|
<button class="fluid ui button" type="button" v-on:click="submit">{{$L('submit query')}}</button>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<tr v-for="(proc, index) in executings">
|
<tr v-for="(proc, index) in executings">
|
||||||
<td class="center aligned"><router-link :to="'/job/edit/'+proc.group+'/'+proc.jobId">{{proc.jobId}}</router-link></td>
|
<td class="center aligned"><router-link :to="'/job/edit/'+proc.group+'/'+proc.jobId">{{proc.jobId}}</router-link></td>
|
||||||
<td class="center aligned">{{proc.group}}</td>
|
<td class="center aligned">{{proc.group}}</td>
|
||||||
<td class="center aligned">{{$store.getters.getHostnameByID(proc.nodeId)}}</td>
|
<td class="center aligned">{{$store.getters.hostshows(proc.nodeId)}}</td>
|
||||||
<td class="center aligned">{{proc.id}}</td>
|
<td class="center aligned">{{proc.id}}</td>
|
||||||
<td class="center aligned">{{proc.time}}</td>
|
<td class="center aligned">{{proc.time}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -56,7 +56,7 @@ export default {
|
||||||
name: 'job-executing',
|
name: 'job-executing',
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
prefetchs: {groups: [], nodes: []},
|
prefetchs: {groups: []},
|
||||||
loading: false,
|
loading: false,
|
||||||
groups: [],
|
groups: [],
|
||||||
ids: '',
|
ids: '',
|
||||||
|
@ -76,12 +76,6 @@ export default {
|
||||||
vm.prefetchs.groups = resp;
|
vm.prefetchs.groups = resp;
|
||||||
this.fetchList(this.buildQuery());
|
this.fetchList(this.buildQuery());
|
||||||
}).do();
|
}).do();
|
||||||
|
|
||||||
this.$rest.GET('nodes').onsucceed(200, (resp)=>{
|
|
||||||
for (var i in resp) {
|
|
||||||
vm.prefetchs.nodes.push(resp[i].id);
|
|
||||||
}
|
|
||||||
}).do();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form class="ui form" method="GET" v-bind:class="{loading:loading}" v-on:submit.prevent>
|
<form class="ui form" method="GET" v-bind:class="{loading:loading}" v-on:submit.prevent v-on:keyup.enter="submit">
|
||||||
<div class="two fields">
|
<div class="two fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{$L('job name')}}</label>
|
<label>{{$L('job name')}}</label>
|
||||||
|
@ -13,7 +13,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{$L('node')}}</label>
|
<label>{{$L('node')}}</label>
|
||||||
<input type="text" v-model="hostnames" :placeholder="$L('multiple Hostnames can separated by commas')">
|
<input v-if="$store.getters.showWithHostname" type="text" v-model="hostnames" :placeholder="$L('multiple Hostnames can separated by commas')">
|
||||||
|
<input v-else type="text" v-model="ips" :placeholder="$L('multiple IPs can separated by commas')">
|
||||||
</div>
|
</div>
|
||||||
<div class="two fields">
|
<div class="two fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -56,7 +57,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="log in list">
|
<tr v-for="log in list">
|
||||||
<td><router-link class="item" :to="'/job/edit/'+log.jobGroup+'/'+log.jobId">{{log.name}}</router-link></td>
|
<td><router-link class="item" :to="'/job/edit/'+log.jobGroup+'/'+log.jobId">{{log.name}}</router-link></td>
|
||||||
<td :title="log.node">{{$store.getters.getHostnameByID(log.node)}}</td>
|
<td :title="log.node">{{$store.getters.hostshows(log.node)}}</td>
|
||||||
<td>{{log.user}}</td>
|
<td>{{log.user}}</td>
|
||||||
<td :class="{warning: durationAttention(log.beginTime, log.endTime)}"><i class="attention icon" v-if="durationAttention(log.beginTime, log.endTime)"></i> {{formatTime(log)}}</td>
|
<td :class="{warning: durationAttention(log.beginTime, log.endTime)}"><i class="attention icon" v-if="durationAttention(log.beginTime, log.endTime)"></i> {{formatTime(log)}}</td>
|
||||||
<td :class="{error: !log.success}">
|
<td :class="{error: !log.success}">
|
||||||
|
@ -84,6 +85,7 @@ export default {
|
||||||
names: '',
|
names: '',
|
||||||
ids: '',
|
ids: '',
|
||||||
hostnames: '',
|
hostnames: '',
|
||||||
|
ips: '',
|
||||||
begin: '',
|
begin: '',
|
||||||
end: '',
|
end: '',
|
||||||
latest: false,
|
latest: false,
|
||||||
|
@ -115,6 +117,7 @@ export default {
|
||||||
this.names = this.$route.query.names || '';
|
this.names = this.$route.query.names || '';
|
||||||
this.ids = this.$route.query.ids || '';
|
this.ids = this.$route.query.ids || '';
|
||||||
this.hostnames = this.$route.query.hostnames || '';
|
this.hostnames = this.$route.query.hostnames || '';
|
||||||
|
this.ips = this.$route.query.ips || '';
|
||||||
this.begin = this.$route.query.begin || '';
|
this.begin = this.$route.query.begin || '';
|
||||||
this.end = this.$route.query.end || '';
|
this.end = this.$route.query.end || '';
|
||||||
this.page = this.$route.query.page || 1;
|
this.page = this.$route.query.page || 1;
|
||||||
|
@ -138,8 +141,9 @@ export default {
|
||||||
buildQuery(){
|
buildQuery(){
|
||||||
var params = [];
|
var params = [];
|
||||||
if (this.names) params.push('names='+this.names);
|
if (this.names) params.push('names='+this.names);
|
||||||
if (this.ids) params.push('ids='+this.ids);
|
if (!this.$store.getters.showWithHostname && this.ids) params.push('ids='+this.ids);
|
||||||
if (this.hostnames) params.push('hostnames='+this.hostnames);
|
if (this.$store.getters.showWithHostname && this.hostnames) params.push('hostnames='+this.hostnames);
|
||||||
|
if (this.ips) params.push('ips='+this.ips);
|
||||||
if (this.begin) params.push('begin='+this.begin);
|
if (this.begin) params.push('begin='+this.begin);
|
||||||
if (this.end) params.push('end='+this.end);
|
if (this.end) params.push('end='+this.end);
|
||||||
if (this.failedOnly) params.push('failedOnly=true');
|
if (this.failedOnly) params.push('failedOnly=true');
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
<div v-for="(node, nodeIndex) in group.nodes" class="node" v-bind:title="node.title">
|
<div v-for="(node, nodeIndex) in group.nodes" class="node" v-bind:title="node.title">
|
||||||
<router-link class="item" :to="'/job?node='+node.id">
|
<router-link class="item" :to="'/job?node='+node.id">
|
||||||
<i class="red icon fork" v-if="node.version !== version" :title="$L('version inconsistent, node: {version}', node.version)"></i>
|
<i class="red icon fork" v-if="node.version !== version" :title="$L('version inconsistent, node: {version}', node.version)"></i>
|
||||||
{{node.hostname || node.id+"(need to upgrade)"}}
|
{{$store.getters.hostshows(node.id)}}
|
||||||
</router-link>
|
</router-link>
|
||||||
<i v-if="groupIndex != 2" v-on:click="removeConfirm(groupIndex, nodeIndex, node.id)" class="icon remove"></i>
|
<i v-if="groupIndex != 2" v-on:click="removeConfirm(groupIndex, nodeIndex, node.id)" class="icon remove"></i>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,16 +59,12 @@ export default {
|
||||||
{nodes: [], name: 'node offline', title: 'node is in maintenance or is shutdown manually', css:''},
|
{nodes: [], name: 'node offline', title: 'node is in maintenance or is shutdown manually', css:''},
|
||||||
{nodes: [], name: 'node normaly', title: 'node is running', css:'green'}
|
{nodes: [], name: 'node normaly', title: 'node is running', css:'green'}
|
||||||
],
|
],
|
||||||
count: 0,
|
count: 0
|
||||||
version: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function(){
|
mounted: function(){
|
||||||
var vm = this;
|
var vm = this;
|
||||||
this.$rest.GET('version').onsucceed(200, (resp)=>{
|
|
||||||
vm.version = resp;
|
|
||||||
}).do();
|
|
||||||
|
|
||||||
var nodes = this.$store.getters.nodes;
|
var nodes = this.$store.getters.nodes;
|
||||||
for (var id in nodes) {
|
for (var id in nodes) {
|
||||||
|
@ -86,6 +82,12 @@ export default {
|
||||||
vm.count = Object.keys(nodes).length;
|
vm.count = Object.keys(nodes).length;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
version: function(){
|
||||||
|
return this.$store.getters.version;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
removeConfirm: function(groupIndex, nodeIndex, nodeId){
|
removeConfirm: function(groupIndex, nodeIndex, nodeId){
|
||||||
if (!confirm(this.$L('are you sure to remove the node {nodeId}?', nodeId))) return;
|
if (!confirm(this.$L('are you sure to remove the node {nodeId}?', nodeId))) return;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<div class="ui middle large aligned divided list">
|
<div class="ui middle large aligned divided list">
|
||||||
<div class="item" v-for="nodeID in g.nids">
|
<div class="item" v-for="nodeID in g.nids">
|
||||||
<span v-if="nodes[nodeID]">{{nodes[nodeID].hostname || nodes[nodeID].id}}
|
<span v-if="nodes[nodeID]">{{$store.getters.hostshows(nodeID)}}
|
||||||
<i class="arrow circle up icon red" v-if="nodes[nodeID].hostname == ''"></i>
|
<i class="arrow circle up icon red" v-if="nodes[nodeID].hostname == ''"></i>
|
||||||
<i v-if="nodes[nodeID].hostname == ''">(need to upgrade)</i>
|
<i v-if="nodes[nodeID].hostname == ''">(need to upgrade)</i>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -24,6 +24,8 @@ var language = {
|
||||||
'total number of executeds': 'Total executeds',
|
'total number of executeds': 'Total executeds',
|
||||||
'total number of nodes': 'Total nodes',
|
'total number of nodes': 'Total nodes',
|
||||||
'job executed in past 7 days': 'Job executed in past 7 days',
|
'job executed in past 7 days': 'Job executed in past 7 days',
|
||||||
|
'node show as': 'Node show as',
|
||||||
|
'hostname': 'Hostname',
|
||||||
|
|
||||||
'batch': 'Batch',
|
'batch': 'Batch',
|
||||||
'job name': 'Job name',
|
'job name': 'Job name',
|
||||||
|
@ -31,6 +33,7 @@ var language = {
|
||||||
'job ID': 'Job ID',
|
'job ID': 'Job ID',
|
||||||
'multiple IDs can separated by commas': 'Multiple IDs can separated by commas',
|
'multiple IDs can separated by commas': 'Multiple IDs can separated by commas',
|
||||||
'multiple Hostnames can separated by commas': 'Multiple Hostnames can separated by commas',
|
'multiple Hostnames can separated by commas': 'Multiple Hostnames can separated by commas',
|
||||||
|
'multiple IPs can separated by commas': 'Multiple IPs can separated by commas',
|
||||||
'starting date': 'Starting date',
|
'starting date': 'Starting date',
|
||||||
'end date': 'End date',
|
'end date': 'End date',
|
||||||
'failure only': 'Failure only',
|
'failure only': 'Failure only',
|
||||||
|
|
|
@ -24,6 +24,8 @@ var language = {
|
||||||
'total number of executeds': '执行任务总次数',
|
'total number of executeds': '执行任务总次数',
|
||||||
'total number of nodes': '节点总数',
|
'total number of nodes': '节点总数',
|
||||||
'job executed in past 7 days': '过去 7 天任务统计',
|
'job executed in past 7 days': '过去 7 天任务统计',
|
||||||
|
'node show as': '节点显示为',
|
||||||
|
'hostname': '主机名称',
|
||||||
|
|
||||||
'batch': '批量',
|
'batch': '批量',
|
||||||
'job name': '任务名称',
|
'job name': '任务名称',
|
||||||
|
@ -31,6 +33,7 @@ var language = {
|
||||||
'job ID': '任务 ID',
|
'job ID': '任务 ID',
|
||||||
'multiple IDs can separated by commas': '多个 ID 用英文逗号分隔',
|
'multiple IDs can separated by commas': '多个 ID 用英文逗号分隔',
|
||||||
'multiple Hostnames can separated by commas': '多个主机名称用英文逗号分隔',
|
'multiple Hostnames can separated by commas': '多个主机名称用英文逗号分隔',
|
||||||
|
'multiple IPs can separated by commas': '多个 IP 用英文逗号分隔',
|
||||||
'starting date': '起始日期',
|
'starting date': '起始日期',
|
||||||
'end date': '截至日期',
|
'end date': '截至日期',
|
||||||
'failure only': '只看失败的任务',
|
'failure only': '只看失败的任务',
|
||||||
|
|
|
@ -124,6 +124,10 @@ var initConf = new Promise((resolve) => {
|
||||||
store.commit('setEmail', resp.email);
|
store.commit('setEmail', resp.email);
|
||||||
store.commit('setRole', resp.role);
|
store.commit('setRole', resp.role);
|
||||||
|
|
||||||
|
restApi.GET('version').onsucceed(200, (resp)=>{
|
||||||
|
store.commit('setVersion', resp);
|
||||||
|
}).do();
|
||||||
|
|
||||||
restApi.GET('configurations').
|
restApi.GET('configurations').
|
||||||
onsucceed(200, (resp) => {
|
onsucceed(200, (resp) => {
|
||||||
Vue.use((Vue) => Vue.prototype.$appConfig = resp);
|
Vue.use((Vue) => Vue.prototype.$appConfig = resp);
|
||||||
|
|
|
@ -5,16 +5,21 @@ Vue.use(Vuex);
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
|
version: '',
|
||||||
enabledAuth: false,
|
enabledAuth: false,
|
||||||
user: {
|
user: {
|
||||||
email: '',
|
email: '',
|
||||||
role: 0
|
role: 0
|
||||||
},
|
},
|
||||||
nodes: {},
|
nodes: {},
|
||||||
dropdownNodes: []
|
showWithHostname: false
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
|
version: function (state) {
|
||||||
|
return state.version;
|
||||||
|
},
|
||||||
|
|
||||||
email: function (state) {
|
email: function (state) {
|
||||||
return state.user.email;
|
return state.user.email;
|
||||||
},
|
},
|
||||||
|
@ -31,11 +36,16 @@ const store = new Vuex.Store({
|
||||||
return state.nodes;
|
return state.nodes;
|
||||||
},
|
},
|
||||||
|
|
||||||
getHostnameByID: function (state) {
|
showWithHostname: function (state) {
|
||||||
return (id) => {
|
return state.showWithHostname;
|
||||||
if (!state.nodes[id]) return id + '(node not found)';
|
},
|
||||||
return state.nodes[id].hostname || id + '(need to upgrade)';
|
|
||||||
}
|
hostshows: function (state) {
|
||||||
|
return (id) => _hostshows(id, state, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
hostshowsWithoutTip: function (state) {
|
||||||
|
return (id) => _hostshows(id, state, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
getNodeByID: function (state) {
|
getNodeByID: function (state) {
|
||||||
|
@ -45,11 +55,23 @@ const store = new Vuex.Store({
|
||||||
},
|
},
|
||||||
|
|
||||||
dropdownNodes: function (state) {
|
dropdownNodes: function (state) {
|
||||||
return state.dropdownNodes;
|
var dn = [];
|
||||||
|
var nodes = state.nodes;
|
||||||
|
for (var i in nodes) {
|
||||||
|
dn.push({
|
||||||
|
value: nodes[i].id,
|
||||||
|
name: _hostshows(nodes[i].id, state, true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return dn;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setVersion: function (state, v) {
|
||||||
|
state.version = v;
|
||||||
|
},
|
||||||
|
|
||||||
setEmail: function (state, email) {
|
setEmail: function (state, email) {
|
||||||
state.user.email = email;
|
state.user.email = email;
|
||||||
},
|
},
|
||||||
|
@ -64,13 +86,26 @@ const store = new Vuex.Store({
|
||||||
|
|
||||||
setNodes: function (state, nodes) {
|
setNodes: function (state, nodes) {
|
||||||
state.nodes = nodes;
|
state.nodes = nodes;
|
||||||
var dn = []
|
},
|
||||||
for (var i in nodes) {
|
|
||||||
dn.push({value: nodes[i].id, name: nodes[i].hostname || nodes[i].id + '(need to upgrade)'})
|
setShowWithHostname: function (state, b) {
|
||||||
}
|
state.showWithHostname = b;
|
||||||
state.dropdownNodes = dn;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function _hostshows(id, state, tip) {
|
||||||
|
if (!state.nodes[id]) {
|
||||||
|
if (tip) id += '(node not found)';
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var show = state.showWithHostname ? state.nodes[id].hostname : state.nodes[id].ip;
|
||||||
|
if (!show) {
|
||||||
|
show = id
|
||||||
|
if (tip) show += '(need to upgrade)';
|
||||||
|
}
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
export default store
|
export default store
|
||||||
|
|
Loading…
Reference in New Issue