mirror of https://github.com/shunfei/cronsun
Resolved #32
parent
0104aa1bd6
commit
002d565e17
7
node.go
7
node.go
|
@ -90,6 +90,13 @@ func GetNodesBy(query interface{}) (nodes []*Node, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveNode(query interface{}) error {
|
||||||
|
return mgoDB.WithC(Coll_Node, func(c *mgo.Collection) error {
|
||||||
|
return c.Remove(query)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func ISNodeFault(id string) (bool, error) {
|
func ISNodeFault(id string) (bool, error) {
|
||||||
n := 0
|
n := 0
|
||||||
err := mgoDB.WithC(Coll_Node, func(c *mgo.Collection) error {
|
err := mgoDB.WithC(Coll_Node, func(c *mgo.Collection) error {
|
||||||
|
|
30
web/node.go
30
web/node.go
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
v3 "github.com/coreos/etcd/clientv3"
|
v3 "github.com/coreos/etcd/clientv3"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
|
||||||
"github.com/shunfei/cronsun"
|
"github.com/shunfei/cronsun"
|
||||||
"github.com/shunfei/cronsun/conf"
|
"github.com/shunfei/cronsun/conf"
|
||||||
|
@ -163,3 +164,32 @@ func (n *Node) GetNodes(ctx *Context) {
|
||||||
|
|
||||||
outJSONWithCode(ctx.W, http.StatusOK, nodes)
|
outJSONWithCode(ctx.W, http.StatusOK, nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteNode force remove node (by ip) which state in offline or damaged.
|
||||||
|
func (n *Node) DeleteNode(ctx *Context) {
|
||||||
|
vars := mux.Vars(ctx.R)
|
||||||
|
ip := strings.TrimSpace(vars["ip"])
|
||||||
|
if len(ip) == 0 {
|
||||||
|
outJSONWithCode(ctx.W, http.StatusBadRequest, "node ip is required.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cronsun.DefalutClient.Get(conf.Config.Node + ip)
|
||||||
|
if err != nil {
|
||||||
|
outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Kvs) > 0 {
|
||||||
|
outJSONWithCode(ctx.W, http.StatusBadRequest, "can not remove a running node.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cronsun.RemoveNode(bson.M{"_id": ip})
|
||||||
|
if err != nil {
|
||||||
|
outJSONWithCode(ctx.W, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outJSONWithCode(ctx.W, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ func initRouters() (s *http.Server, err error) {
|
||||||
|
|
||||||
h = NewAuthHandler(nodeHandler.GetNodes)
|
h = NewAuthHandler(nodeHandler.GetNodes)
|
||||||
subrouter.Handle("/nodes", h).Methods("GET")
|
subrouter.Handle("/nodes", h).Methods("GET")
|
||||||
|
h = NewAuthHandler(nodeHandler.DeleteNode)
|
||||||
|
subrouter.Handle("/node/{ip}", h).Methods("DELETE")
|
||||||
// get node group list
|
// get node group list
|
||||||
h = NewAuthHandler(nodeHandler.GetGroups)
|
h = NewAuthHandler(nodeHandler.GetGroups)
|
||||||
subrouter.Handle("/node/groups", h).Methods("GET")
|
subrouter.Handle("/node/groups", h).Methods("GET")
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,32 +1,50 @@
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.node {
|
.node {
|
||||||
width: 130px;
|
width: 140px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 4px 0;
|
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node > i.icon.remove {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #2185D0;
|
||||||
|
bottom: 0;
|
||||||
|
display: none;
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
height: auto;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.node:hover > i.icon.remove {display: block;}
|
||||||
</style>
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
<router-link class="ui right floated primary button" to="/node/group"><i class="cubes icon"></i> {{$L('group manager')}}</router-link>
|
<router-link class="ui right floated primary button" to="/node/group"><i class="cubes icon"></i> {{$L('group manager')}}</router-link>
|
||||||
<div class="ui label"
|
<div class="ui label"
|
||||||
<div class="ui label" v-for="item in items" v-bind:title="$L(item.title)">
|
<div class="ui label" v-for="group in groups" v-bind:title="$L(group.title)">
|
||||||
<i class="cube icon" v-bind:class="item.css"></i> {{item.nodes.length}} {{$L(item.name)}}
|
<i class="cube icon" v-bind:class="group.css"></i> {{group.nodes.length}} {{$L(group.name)}}
|
||||||
</div>
|
</div>
|
||||||
{{$L('(total {n} nodes)', count)}}
|
{{$L('(total {n} nodes)', count)}}
|
||||||
<div class="ui label" :title="$L('currently version')"> {{version}} </div>
|
<div class="ui label" :title="$L('currently version')"> {{version}} </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui relaxed list" v-for="item in items">
|
<div class="ui relaxed list" v-for="(group, groupIndex) in groups">
|
||||||
<h4 v-if="item.nodes.length > 0" class="ui horizontal divider header"><i class="cube icon" v-bind:class="item.css"></i> {{$L(item.name)}} {{item.nodes.length}}</h4>
|
<h4 v-if="group.nodes.length > 0" class="ui horizontal divider header"><i class="cube icon" v-bind:class="group.css"></i> {{$L(group.name)}} {{group.nodes.length}}</h4>
|
||||||
<div v-for="node in item.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.id}}
|
{{node.id}}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<i v-if="groupIndex != 2" v-on:click="removeConfirm(groupIndex, nodeIndex, node.id)" class="icon remove"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,10 +55,10 @@ export default {
|
||||||
name: 'node',
|
name: 'node',
|
||||||
data: function(){
|
data: function(){
|
||||||
return {
|
return {
|
||||||
items: [
|
groups: [
|
||||||
{nodes:[],name:'node damaged',title:'node can not be deceted due to itself or network etc.',css:'red'},
|
{nodes: [], name: 'node damaged', title: 'node can not be deceted due to itself or network etc.', css:'red'},
|
||||||
{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: ''
|
version: ''
|
||||||
|
@ -67,15 +85,26 @@ export default {
|
||||||
var n = resp[i];
|
var n = resp[i];
|
||||||
n.title = n.version + "\nstarted at: " + n.up
|
n.title = n.version + "\nstarted at: " + n.up
|
||||||
if (n.alived && n.connected) {
|
if (n.alived && n.connected) {
|
||||||
vm.items[2].nodes.push(n);
|
vm.groups[2].nodes.push(n);
|
||||||
} else if (n.alived && !n.connected) {
|
} else if (n.alived && !n.connected) {
|
||||||
vm.items[0].nodes.push(n);
|
vm.groups[0].nodes.push(n);
|
||||||
} else {
|
} else {
|
||||||
vm.items[1].nodes.push(n);
|
vm.groups[1].nodes.push(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vm.count = resp.length || 0;
|
vm.count = resp.length || 0;
|
||||||
}).do();
|
}).do();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
removeConfirm: function(groupIndex, nodeIndex, nodeId){
|
||||||
|
if (!confirm(this.$L('are you sure to remove the node {nodeId}?', nodeId))) return;
|
||||||
|
|
||||||
|
var vm = this;
|
||||||
|
this.$rest.DELETE('node/'+nodeId).onsucceed(204, (resp)=>{
|
||||||
|
vm.groups[groupIndex].nodes.splice(nodeIndex, 1);
|
||||||
|
}).do();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -129,7 +129,8 @@ var language = {
|
||||||
'include nodes': 'Include nodes',
|
'include nodes': 'Include nodes',
|
||||||
'select nodes': 'Select nodes',
|
'select nodes': 'Select nodes',
|
||||||
'select groups': 'Select groups',
|
'select groups': 'Select groups',
|
||||||
'are you sure to delete the group {name}?': 'Are you sure to delete the group {0}?'
|
'are you sure to delete the group {name}?': 'Are you sure to delete the group {0}?',
|
||||||
|
'are you sure to remove the node {nodeId}?': 'Are you sure to remove the node {0}?'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default language;
|
export default language;
|
||||||
|
|
|
@ -131,7 +131,8 @@ var language = {
|
||||||
'include nodes': '包含的节点',
|
'include nodes': '包含的节点',
|
||||||
'select nodes': '选择节点',
|
'select nodes': '选择节点',
|
||||||
'select groups': '选择分组',
|
'select groups': '选择分组',
|
||||||
'are you sure to delete the group {name}?': '确定删除分组 {0}?'
|
'are you sure to delete the group {name}?': '确定删除分组 {0}?',
|
||||||
|
'are you sure to remove the node {nodeId}?': '确定删除节点 {0}?'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default language;
|
export default language;
|
||||||
|
|
Loading…
Reference in New Issue