mirror of https://github.com/ouqiang/gocron
完善任务列表、主机列表、任务日志列表
parent
2450d193f3
commit
2b0e66dd04
|
@ -5,4 +5,4 @@ gathering = explicit
|
|||
; 默认模块
|
||||
module_name = shell
|
||||
|
||||
host_key_checking = false
|
||||
host_key_checking = false
|
||||
|
|
|
@ -49,6 +49,16 @@ func (host *Host) List() ([]Host, error) {
|
|||
return list, err
|
||||
}
|
||||
|
||||
func (host *Host) AllList() ([]Host, error) {
|
||||
host.parsePageAndPageSize()
|
||||
list := make([]Host, 0)
|
||||
err := Db.Desc("id").Find(&list)
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (host *Host) Total() (int64, error) {
|
||||
return Db.Count(host)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ package ansible
|
|||
import (
|
||||
"errors"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ansible是否有安装
|
||||
|
@ -32,7 +32,15 @@ func ExecCommand(hosts string, hostFile string, module string, args ...string) (
|
|||
if len(args) > 0 {
|
||||
commandArgs = append(commandArgs, args...)
|
||||
}
|
||||
output, err = utils.ExecShell("ansible", commandArgs...)
|
||||
retryTimes := 2
|
||||
for i := 0; i < retryTimes; i++ {
|
||||
output, err = utils.ExecShell("ansible", commandArgs...)
|
||||
// todo 偶尔出现Failed to connect to the host via ssh. 暂时重试解决
|
||||
if err == nil || !strings.HasPrefix(err.Error(), "exit status 3") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ func (h *Hosts) GetFilename() string {
|
|||
// 写入hosts
|
||||
func (h *Hosts) Write() {
|
||||
host := new(models.Host)
|
||||
hostModels, err := host.List()
|
||||
hostModels, err := host.AllList()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
|
|
|
@ -8,8 +8,19 @@ import (
|
|||
"github.com/ouqiang/gocron/modules/ansible"
|
||||
)
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hosts, err := hostModel.List()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Title"] = "主机列表"
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.HTML(200, "host/index")
|
||||
}
|
||||
|
||||
func Create(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "主机管理"
|
||||
ctx.Data["Title"] = "添加主机"
|
||||
ctx.HTML(200, "host/create")
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/routers/task"
|
||||
"github.com/ouqiang/gocron/routers/host"
|
||||
"github.com/ouqiang/gocron/routers/tasklog"
|
||||
)
|
||||
|
||||
// 路由注册
|
||||
|
@ -14,7 +15,6 @@ func Register(m *macaron.Macaron) {
|
|||
m.SetAutoHead(true)
|
||||
// 404错误
|
||||
m.NotFound(func(ctx *macaron.Context) {
|
||||
// 判断是否get请求还是post, get返回页面, post返回json
|
||||
ctx.HTML(404, "error/404")
|
||||
})
|
||||
// 50x错误
|
||||
|
@ -40,17 +40,15 @@ func Register(m *macaron.Macaron) {
|
|||
m.Group("/task", func() {
|
||||
m.Get("/create", task.Create)
|
||||
m.Post("/store", binding.Bind(task.TaskForm{}), task.Store)
|
||||
m.Get("", task.Index)
|
||||
m.Get("/log", tasklog.Index)
|
||||
})
|
||||
|
||||
// 主机
|
||||
m.Group("/host", func() {
|
||||
m.Get("/create", host.Create)
|
||||
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
||||
})
|
||||
|
||||
// 任务日志
|
||||
m.Group("/tasklog/", func() {
|
||||
|
||||
m.Get("", host.Index)
|
||||
})
|
||||
|
||||
// API接口
|
||||
|
@ -58,3 +56,17 @@ func Register(m *macaron.Macaron) {
|
|||
|
||||
})
|
||||
}
|
||||
|
||||
func isAjaxRequest(ctx *macaron.Context) bool {
|
||||
req := ctx.Req.Header.Get("X-Requested-With")
|
||||
if req == "XMLHttpRequest" {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isGetRequest(ctx *macaron.Context) bool {
|
||||
return ctx.Req.Method == "GET"
|
||||
}
|
|
@ -9,6 +9,18 @@ import (
|
|||
"github.com/ouqiang/gocron/service"
|
||||
)
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
taskModel := new(models.Task)
|
||||
tasks, err := taskModel.List()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Title"] = "任务列表"
|
||||
ctx.Data["Tasks"] = tasks
|
||||
ctx.Data["URI"] = "/task"
|
||||
ctx.HTML(200, "task/index")
|
||||
}
|
||||
|
||||
func Create(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hosts, err := hostModel.List()
|
||||
|
@ -16,9 +28,10 @@ func Create(ctx *macaron.Context) {
|
|||
logger.Error(err)
|
||||
ctx.Redirect("/host/create")
|
||||
}
|
||||
logger.Info(hosts)
|
||||
logger.Debug(hosts)
|
||||
ctx.Data["Title"] = "任务管理"
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["URI"] = "/task/create"
|
||||
ctx.Data["FirstHostName"] = hosts[0].Name
|
||||
ctx.Data["FirstHostId"] = hosts[0].Id
|
||||
ctx.HTML(200, "task/create")
|
||||
|
@ -35,6 +48,7 @@ type TaskForm struct {
|
|||
Remark string
|
||||
}
|
||||
|
||||
// 保存任务
|
||||
func Store(ctx *macaron.Context, form TaskForm) string {
|
||||
hosts := ctx.Req.Form["hosts[]"]
|
||||
taskModel := models.Task{}
|
||||
|
@ -59,3 +73,8 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
|||
|
||||
return json.Success("保存成功", nil)
|
||||
}
|
||||
|
||||
// 删除任务
|
||||
func Remove(ctx *macaron.Context) {
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package tasklog
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
)
|
||||
|
||||
// @author qiang.ou<qingqianludao@gmail.com>
|
||||
// @date 2017/4/7-21:18
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
logModel := new(models.TaskLog)
|
||||
logs, err := logModel.List()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Title"] = "任务日志"
|
||||
ctx.Data["Logs"] = logs
|
||||
ctx.Data["URI"] = "/task/log"
|
||||
ctx.HTML(200, "task/log")
|
||||
}
|
|
@ -172,8 +172,17 @@ func createHandlerJob(taskModel models.Task) cron.FuncJob {
|
|||
logger.Error("写入任务日志失败-", err)
|
||||
return
|
||||
}
|
||||
// err != nil 执行失败
|
||||
result, err := handler.Run(taskModel)
|
||||
// err != nil 执行失败, 失败重试3次
|
||||
retryTime := 4
|
||||
var result string = ""
|
||||
for i := 0; i < retryTime; i++ {
|
||||
result, err = handler.Run(taskModel)
|
||||
if err == nil {
|
||||
break
|
||||
} else {
|
||||
logger.Error("执行失败#tasklog.id-" + strconv.Itoa(taskLogId) + "#尝试次数-" + strconv.Itoa(i + 1) + "#" + err.Error() + " " + result)
|
||||
}
|
||||
}
|
||||
_, err = updateTaskLog(int(taskLogId), result, err)
|
||||
if err != nil {
|
||||
logger.Error("更新任务日志失败-", err)
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
onSuccess: function(event, fields) {
|
||||
var util = new Util();
|
||||
util.post('/host/store', fields, function(code, message) {
|
||||
location.reload();
|
||||
location.href = "/host"
|
||||
});
|
||||
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
<!--the vertical menu-->
|
||||
{{{ template "host/menu" . }}}
|
||||
|
||||
<div class="twelve wide column">
|
||||
<div class="pageHeader">
|
||||
<div class="segment">
|
||||
<h3 class="ui dividing header">
|
||||
<i class="large add icon"></i>
|
||||
<div class="content">
|
||||
添加主机
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui single line table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>主机名</th>
|
||||
<th>别名</th>
|
||||
<th>用户名</th>
|
||||
<th>密码</th>
|
||||
<th>端口</th>
|
||||
<th>认证方式</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{range $i, $v := .Hosts}}}
|
||||
<tr>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Alias}}}</td>
|
||||
<td>{{{.Username}}}</td>
|
||||
<td>{{{.Password}}}</td>
|
||||
<td>{{{.Port}}}</td>
|
||||
<td>{{{.LoginType}}}</td>
|
||||
<td>{{{.Remark}}}</td>
|
||||
</tr>
|
||||
{{{end}}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--the newDevice form-->
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
var util = new Util();
|
||||
util.post('/host/store', fields, function(code, message) {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
identifier : 'name',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机名'
|
||||
}
|
||||
]
|
||||
},
|
||||
alias: {
|
||||
identifier : 'alias',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机别名'
|
||||
}
|
||||
]
|
||||
},
|
||||
username: {
|
||||
identifier : 'username',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入SSH用户名'
|
||||
}
|
||||
]
|
||||
},
|
||||
port: {
|
||||
identifier : 'port',
|
||||
rules: [
|
||||
{
|
||||
type : 'integer',
|
||||
prompt : '请输入SSH端口'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
|
@ -107,6 +107,7 @@
|
|||
onSuccess: function(event, fields) {
|
||||
var util = new Util();
|
||||
util.post('/task/store', fields, function(code, message) {
|
||||
location.href = "/task"
|
||||
});
|
||||
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
<!--the vertical menu-->
|
||||
|
||||
{{{template "task/menu" .}}}
|
||||
<div class="twelve wide column">
|
||||
<div class="pageHeader">
|
||||
<div class="segment">
|
||||
<h3 class="ui dividing header">
|
||||
<i class="tasks icon"></i>
|
||||
<div class="content">
|
||||
任务列表
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui single line table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务名称</th>
|
||||
<th>cron表达式</th>
|
||||
<th>协议</th>
|
||||
<th>任务类型</th>
|
||||
<th>命令</th>
|
||||
<th>超时时间(秒)</th>
|
||||
<th>延迟时间(秒)</th>
|
||||
<th>主机</th>
|
||||
<th>备注</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{range $i, $v := .Tasks}}}
|
||||
<tr>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Spec}}}</td>
|
||||
<td>{{{.Protocol}}}</td>
|
||||
<td>{{{.Type}}}</td>
|
||||
<td>{{{.Command}}}</td>
|
||||
<td>{{{.Timeout}}}</td>
|
||||
<td>{{{.Delay}}}</td>
|
||||
<td>{{{.SshHosts}}}</td>
|
||||
<td>{{{.Remark}}}</td>
|
||||
<td>{{{.Status}}}</td>
|
||||
</tr>
|
||||
{{{end}}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--the newDevice form-->
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$('.ui.checkbox')
|
||||
.checkbox()
|
||||
;
|
||||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
var util = new Util();
|
||||
util.post('/task/store', fields, function(code, message) {
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
identifier : 'name',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入任务名称'
|
||||
}
|
||||
]
|
||||
},
|
||||
spec: {
|
||||
identifier : 'spec',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入crontab格式表达式'
|
||||
}
|
||||
]
|
||||
},
|
||||
command: {
|
||||
identifier : 'command',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入任务命令'
|
||||
}
|
||||
]
|
||||
}, hosts: {
|
||||
identifier : 'hosts',
|
||||
rules: [
|
||||
{
|
||||
type : 'checked',
|
||||
prompt : '请选择主机'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
|
@ -0,0 +1,112 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
<!--the vertical menu-->
|
||||
{{{ template "task/menu" . }}}
|
||||
|
||||
<div class="twelve wide column">
|
||||
<div class="pageHeader">
|
||||
<div class="segment">
|
||||
<h3 class="ui dividing header">
|
||||
<i class="large add icon"></i>
|
||||
<div class="content">
|
||||
任务日志
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui single line table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务名称</th>
|
||||
<th>cron表达式</th>
|
||||
<th>协议</th>
|
||||
<th>任务类型</th>
|
||||
<th>命令</th>
|
||||
<th>超时时间(秒)</th>
|
||||
<th>延迟时间(秒)</th>
|
||||
<th>主机</th>
|
||||
<th>开始时间</th>
|
||||
<th>结束时间</th>
|
||||
<th>状态</th>
|
||||
<th>执行结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{range $i, $v := .Logs}}}
|
||||
<tr>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Spec}}}</td>
|
||||
<td>{{{.Protocol}}}</td>
|
||||
<td>{{{.Type}}}</td>
|
||||
<td>{{{.Command}}}</td>
|
||||
<td>{{{.Timeout}}}</td>
|
||||
<td>{{{.Delay}}}</td>
|
||||
<td>{{{.SshHosts}}}</td>
|
||||
<td>{{{.StartTime}}}</td>
|
||||
<td>{{{.EndTime}}}</td>
|
||||
<td>{{{.Status}}}</td>
|
||||
<td>{{{.Result}}}</td>
|
||||
</tr>
|
||||
{{{end}}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--the newDevice form-->
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
var util = new Util();
|
||||
util.post('/host/store', fields, function(code, message) {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
identifier : 'name',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机名'
|
||||
}
|
||||
]
|
||||
},
|
||||
alias: {
|
||||
identifier : 'alias',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机别名'
|
||||
}
|
||||
]
|
||||
},
|
||||
username: {
|
||||
identifier : 'username',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入SSH用户名'
|
||||
}
|
||||
]
|
||||
},
|
||||
port: {
|
||||
identifier : 'port',
|
||||
rules: [
|
||||
{
|
||||
type : 'integer',
|
||||
prompt : '请输入SSH端口'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
|
@ -1,13 +1,13 @@
|
|||
<div class="four wide column">
|
||||
<div class="three wide column">
|
||||
<div class="verticalMenu">
|
||||
<div class="ui vertical pointing menu fluid">
|
||||
<a class="active teal item" href="/task">
|
||||
<a class="{{{if eq .URI "/task"}}}active teal{{{end}}} item" href="/task">
|
||||
<i class="tasks icon"></i> 任务列表
|
||||
</a>
|
||||
<a class="item" href="/task/create">
|
||||
<a class="item {{{if eq .URI "/task/create"}}}active teal{{{end}}} " href="/task/create">
|
||||
<i class="plus icon"></i> 添加任务
|
||||
</a>
|
||||
<a class="item" href="">
|
||||
<a class="item {{{if eq .URI "/task/log"}}}active teal{{{end}}} " href="/task/log">
|
||||
<i class="bar chart icon"></i> 任务日志
|
||||
</a>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue