支持多台服务器同时执行任务+优化页面

一个定时任务可以允许多台服务器同时执行任务,优化页面关闭并刷新列表页
This commit is contained in:
georgehao
2019-04-16 23:40:07 +08:00
parent 07aff7ee72
commit 830d49ee1c
24 changed files with 391 additions and 137 deletions

View File

@@ -9,11 +9,11 @@ package controllers
import ( import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
"github.com/axgle/mahonia"
"github.com/george518/PPGo_Job/libs" "github.com/george518/PPGo_Job/libs"
"github.com/george518/PPGo_Job/models" "github.com/george518/PPGo_Job/models"
"strconv" "strconv"
"strings" "strings"
"github.com/axgle/mahonia"
) )
const ( const (
@@ -361,4 +361,9 @@ func gbkAsUtf8(str string) string {
resStr := srcDecoder.ConvertString(str) resStr := srcDecoder.ConvertString(str)
_, resBytes, _ := desDecoder.Translate([]byte(resStr), true) _, resBytes, _ := desDecoder.Translate([]byte(resStr), true)
return string(resBytes) return string(resBytes)
} }
//任务识别码
func jobKey(taskId, serverId int) int {
return taskId*10000 + serverId
}

View File

@@ -8,6 +8,7 @@
package controllers package controllers
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -78,6 +79,15 @@ func (self *TaskController) Edit() {
self.Data["notify_user_ids"] = notifyUserIds self.Data["notify_user_ids"] = notifyUserIds
server_ids := strings.Split(task.ServerIds, ",")
var server_ids_arr []int
for _, sv := range server_ids {
i, _ := strconv.Atoi(sv)
server_ids_arr = append(server_ids_arr, i)
}
self.Data["service_ids"] = server_ids_arr
notifyTplList, _, err := models.NotifyTplGetByTplTypeList(task.NotifyType) notifyTplList, _, err := models.NotifyTplGetByTplTypeList(task.NotifyType)
tplList := make([]map[string]interface{}, len(notifyTplList)) tplList := make([]map[string]interface{}, len(notifyTplList))
@@ -145,13 +155,28 @@ func (self *TaskController) Detail() {
// 分组列表 // 分组列表
self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId) self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId)
serverName := "本地服务器" serverName := ""
if task.ServerId == 0 { if task.ServerIds == "0" {
serverName = "本地服务器" serverName = "本地服务器"
} else { } else {
server, err := models.TaskServerGetById(task.ServerId) serverIdSli := strings.Split(task.ServerIds, ",")
if err == nil { for _, v := range serverIdSli {
serverName = server.ServerName if v == "0" {
serverName = "本地服务器 "
}
}
servers, n := models.TaskServerGetByIds(task.ServerIds)
if n > 0 {
for _, server := range servers {
fmt.Println(server.Status)
if server.Status != 0 {
serverName += server.ServerName + "【无效】 "
} else {
serverName += server.ServerName + " "
}
}
} else {
serverName += "服务器异常!!"
} }
} }
@@ -212,7 +237,7 @@ func (self *TaskController) AjaxSave() {
task.TaskName = strings.TrimSpace(self.GetString("task_name")) task.TaskName = strings.TrimSpace(self.GetString("task_name"))
task.Description = strings.TrimSpace(self.GetString("description")) task.Description = strings.TrimSpace(self.GetString("description"))
task.Concurrent, _ = self.GetInt("concurrent") task.Concurrent, _ = self.GetInt("concurrent")
task.ServerId, _ = self.GetInt("server_id") task.ServerIds = strings.TrimSpace(self.GetString("server_ids"))
task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.CronSpec = strings.TrimSpace(self.GetString("cron_spec"))
task.Command = strings.TrimSpace(self.GetString("command")) task.Command = strings.TrimSpace(self.GetString("command"))
task.Timeout, _ = self.GetInt("timeout") task.Timeout, _ = self.GetInt("timeout")
@@ -258,7 +283,7 @@ func (self *TaskController) AjaxSave() {
task.Description = strings.TrimSpace(self.GetString("description")) task.Description = strings.TrimSpace(self.GetString("description"))
task.GroupId, _ = self.GetInt("group_id") task.GroupId, _ = self.GetInt("group_id")
task.Concurrent, _ = self.GetInt("concurrent") task.Concurrent, _ = self.GetInt("concurrent")
task.ServerId, _ = self.GetInt("server_id") task.ServerIds = strings.TrimSpace(self.GetString("server_ids"))
task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.CronSpec = strings.TrimSpace(self.GetString("cron_spec"))
task.Command = strings.TrimSpace(self.GetString("command")) task.Command = strings.TrimSpace(self.GetString("command"))
task.Timeout, _ = self.GetInt("timeout") task.Timeout, _ = self.GetInt("timeout")
@@ -291,8 +316,6 @@ func (self *TaskController) AjaxSave() {
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
//检查是否含有禁用命令 //检查是否含有禁用命令
func checkCommand(command string) (string, bool) { func checkCommand(command string) (string, bool) {
@@ -332,8 +355,6 @@ func (self *TaskController) AjaxNopass() {
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
func (self *TaskController) AjaxStart() { func (self *TaskController) AjaxStart() {
taskId, _ := self.GetInt("id") taskId, _ := self.GetInt("id")
if taskId == 0 { if taskId == 0 {
@@ -349,15 +370,19 @@ func (self *TaskController) AjaxStart() {
self.ajaxMsg("任务状态有误", MSG_ERR) self.ajaxMsg("任务状态有误", MSG_ERR)
} }
job, err := jobs.NewJobFromTask(task) jobArr, err := jobs.NewJobFromTask(task)
if err != nil { if err != nil {
self.ajaxMsg("创建任务失败", MSG_ERR) self.ajaxMsg("创建任务失败", MSG_ERR)
} }
if jobs.AddJob(task.CronSpec, job) { for _, job := range jobArr {
task.Status = 1 if jobs.AddJob(task.CronSpec, job) {
task.Update() task.Status = 1
task.Update()
}
} }
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
@@ -372,9 +397,18 @@ func (self *TaskController) AjaxPause() {
self.ajaxMsg("查不到该任务", MSG_ERR) self.ajaxMsg("查不到该任务", MSG_ERR)
} }
jobs.RemoveJob(taskId) //移出任务
TaskServerIdsArr := strings.Split(task.ServerIds, ",")
for _, server_id := range TaskServerIdsArr {
server_id_int, _ := strconv.Atoi(server_id)
jobKey := jobKey(task.Id, server_id_int)
jobs.RemoveJob(jobKey)
}
task.Status = 0 task.Status = 0
task.Update() task.Update()
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
@@ -387,11 +421,14 @@ func (self *TaskController) AjaxRun() {
self.ajaxMsg(err.Error(), MSG_ERR) self.ajaxMsg(err.Error(), MSG_ERR)
} }
job, err := jobs.NewJobFromTask(task) jobArr, err := jobs.NewJobFromTask(task)
if err != nil { if err != nil {
self.ajaxMsg(err.Error(), MSG_ERR) self.ajaxMsg(err.Error(), MSG_ERR)
} }
job.Run() for _, job := range jobArr {
job.Run()
}
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
@@ -408,9 +445,12 @@ func (self *TaskController) AjaxBatchStart() {
} }
if task, err := models.TaskGetById(id); err == nil { if task, err := models.TaskGetById(id); err == nil {
job, err := jobs.NewJobFromTask(task) jobArr, err := jobs.NewJobFromTask(task)
if err == nil { if err == nil {
jobs.AddJob(task.CronSpec, job) for _, job := range jobArr {
jobs.AddJob(task.CronSpec, job)
}
task.Status = 1 task.Status = 1
task.Update() task.Update()
} }
@@ -430,9 +470,17 @@ func (self *TaskController) AjaxBatchPause() {
if id < 1 { if id < 1 {
continue continue
} }
jobs.RemoveJob(id)
if task, err := models.TaskGetById(id); err == nil { task, err := models.TaskGetById(id)
//移出任务
TaskServerIdsArr := strings.Split(task.ServerIds, ",")
for _, server_id := range TaskServerIdsArr {
server_id_int, _ := strconv.Atoi(server_id)
jobKey := jobKey(task.Id, server_id_int)
jobs.RemoveJob(jobKey)
}
if err == nil {
task.Status = 0 task.Status = 0
task.Update() task.Update()
} }
@@ -451,9 +499,19 @@ func (self *TaskController) AjaxBatchDel() {
if id < 1 { if id < 1 {
continue continue
} }
task, _ := models.TaskGetById(id)
//移出任务
TaskServerIdsArr := strings.Split(task.ServerIds, ",")
for _, server_id := range TaskServerIdsArr {
server_id_int, _ := strconv.Atoi(server_id)
jobKey := jobKey(task.Id, server_id_int)
jobs.RemoveJob(jobKey)
}
models.TaskDel(id) models.TaskDel(id)
models.TaskLogDelByTaskId(id) models.TaskLogDelByTaskId(id)
jobs.RemoveJob(id)
} }
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
@@ -661,12 +719,12 @@ func (self *TaskController) ApiTask() {
task_id, _ := self.GetInt("id") task_id, _ := self.GetInt("id")
if task_id == 0 { if task_id == 0 {
task := new(models.Task) task := new(models.Task)
task.CreateId,_ = self.GetInt("create_id") task.CreateId, _ = self.GetInt("create_id")
task.GroupId, _ = self.GetInt("group_id") task.GroupId, _ = self.GetInt("group_id")
task.TaskName = strings.TrimSpace(self.GetString("task_name")) task.TaskName = strings.TrimSpace(self.GetString("task_name"))
task.Description = strings.TrimSpace(self.GetString("description")) task.Description = strings.TrimSpace(self.GetString("description"))
task.Concurrent, _ = self.GetInt("concurrent") task.Concurrent, _ = self.GetInt("concurrent")
task.ServerId, _ = self.GetInt("server_id") task.ServerIds = strings.TrimSpace(self.GetString("server_ids"))
task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.CronSpec = strings.TrimSpace(self.GetString("cron_spec"))
task.Command = strings.TrimSpace(self.GetString("command")) task.Command = strings.TrimSpace(self.GetString("command"))
task.Timeout, _ = self.GetInt("timeout") task.Timeout, _ = self.GetInt("timeout")
@@ -716,7 +774,7 @@ func (self *TaskController) ApiTask() {
task.Description = strings.TrimSpace(self.GetString("description")) task.Description = strings.TrimSpace(self.GetString("description"))
task.GroupId, _ = self.GetInt("group_id") task.GroupId, _ = self.GetInt("group_id")
task.Concurrent, _ = self.GetInt("concurrent") task.Concurrent, _ = self.GetInt("concurrent")
task.ServerId, _ = self.GetInt("server_id") task.ServerIds = strings.TrimSpace(self.GetString("server_ids"))
task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.CronSpec = strings.TrimSpace(self.GetString("cron_spec"))
task.Command = strings.TrimSpace(self.GetString("command")) task.Command = strings.TrimSpace(self.GetString("command"))
task.Timeout, _ = self.GetInt("timeout") task.Timeout, _ = self.GetInt("timeout")
@@ -724,7 +782,7 @@ func (self *TaskController) ApiTask() {
task.NotifyType, _ = self.GetInt("notify_type") task.NotifyType, _ = self.GetInt("notify_type")
task.NotifyTplId, _ = self.GetInt("notify_tpl_id") task.NotifyTplId, _ = self.GetInt("notify_tpl_id")
task.NotifyUserIds = strings.TrimSpace(self.GetString("notify_user_ids")) task.NotifyUserIds = strings.TrimSpace(self.GetString("notify_user_ids"))
task.UpdateId , _ = self.GetInt("update_id") task.UpdateId, _ = self.GetInt("update_id")
task.Status = 0 //接口不需要 task.Status = 0 //接口不需要
if task.IsNotify == 1 && task.NotifyTplId <= 0 { if task.IsNotify == 1 && task.NotifyTplId <= 0 {
@@ -761,15 +819,18 @@ func (self *TaskController) ApiStart() {
self.ajaxMsg("任务状态有误", MSG_ERR) self.ajaxMsg("任务状态有误", MSG_ERR)
} }
job, err := jobs.NewJobFromTask(task) jobArr, err := jobs.NewJobFromTask(task)
if err != nil { if err != nil {
self.ajaxMsg("创建任务失败", MSG_ERR) self.ajaxMsg("创建任务失败", MSG_ERR)
} }
if jobs.AddJob(task.CronSpec, job) { for _, job := range jobArr {
task.Status = 1 if jobs.AddJob(task.CronSpec, job) {
task.Update() task.Status = 1
task.Update()
}
} }
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }
@@ -784,11 +845,17 @@ func (self *TaskController) ApiPause() {
self.ajaxMsg("查不到该任务", MSG_ERR) self.ajaxMsg("查不到该任务", MSG_ERR)
} }
jobs.RemoveJob(taskId) //移出任务
TaskServerIdsArr := strings.Split(task.ServerIds, ",")
for _, server_id := range TaskServerIdsArr {
server_id_int, _ := strconv.Atoi(server_id)
jobKey := jobKey(task.Id, server_id_int)
jobs.RemoveJob(jobKey)
}
task.Status = 0 task.Status = 0
task.Update() task.Update()
self.ajaxMsg("", MSG_OK) self.ajaxMsg("", MSG_OK)
} }

View File

@@ -8,6 +8,7 @@
package controllers package controllers
import ( import (
"fmt"
"github.com/astaxie/beego" "github.com/astaxie/beego"
"github.com/george518/PPGo_Job/libs" "github.com/george518/PPGo_Job/libs"
"github.com/george518/PPGo_Job/models" "github.com/george518/PPGo_Job/models"
@@ -74,9 +75,12 @@ func (self *TaskLogController) Table() {
for k, v := range result { for k, v := range result {
row := make(map[string]interface{}) row := make(map[string]interface{})
row["id"] = v.Id row["id"] = v.Id
row["task_id"] = v.TaskId row["task_id"] = jobKey(v.TaskId, v.ServerId)
row["start_time"] = beego.Date(time.Unix(v.CreateTime, 0), "Y-m-d H:i:s") row["start_time"] = beego.Date(time.Unix(v.CreateTime, 0), "Y-m-d H:i:s")
row["process_time"] = float64(v.ProcessTime) / 1000 row["process_time"] = float64(v.ProcessTime) / 1000
row["server_id"] = v.ServerId
row["server_name"] = v.ServerName + "#" + strconv.Itoa(v.ServerId)
if v.Status == 0 { if v.Status == 0 {
row["output_size"] = libs.SizeFormat(float64(len(v.Output))) row["output_size"] = libs.SizeFormat(float64(len(v.Output)))
} else { } else {
@@ -99,6 +103,8 @@ func (self *TaskLogController) Detail() {
//日志内容 //日志内容
id, _ := self.GetInt("id") id, _ := self.GetInt("id")
tasklog, err := models.TaskLogGetById(id) tasklog, err := models.TaskLogGetById(id)
fmt.Println(tasklog)
if err != nil { if err != nil {
self.Ctx.WriteString("日志不存在") self.Ctx.WriteString("日志不存在")
return return
@@ -119,6 +125,8 @@ func (self *TaskLogController) Detail() {
row["output_size"] = libs.SizeFormat(float64(len(tasklog.Error))) row["output_size"] = libs.SizeFormat(float64(len(tasklog.Error)))
} }
row["server_name"] = tasklog.ServerName
row["output"] = tasklog.Output row["output"] = tasklog.Output
row["error"] = tasklog.Error row["error"] = tasklog.Error
@@ -149,15 +157,31 @@ func (self *TaskLogController) Detail() {
// 分组列表 // 分组列表
self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId) self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId)
serverName := "本地服务器" serverName := ""
if task.ServerId == 0 { if task.ServerIds == "0" {
serverName = "本地服务器" serverName = "本地服务器"
} else { } else {
server, err := models.TaskServerGetById(task.ServerId)
if err == nil { serverIdSli := strings.Split(task.ServerIds, ",")
serverName = server.ServerName for _, v := range serverIdSli {
if v == "0" {
serverName = "本地服务器 "
}
}
servers, n := models.TaskServerGetByIds(task.ServerIds)
if n > 0 {
for _, server := range servers {
if server.Status != 0 {
serverName += server.ServerName + "【无效】 "
} else {
serverName += server.ServerName + " "
}
}
} else {
serverName = "服务器异常!! "
} }
} }
self.Data["serverName"] = serverName self.Data["serverName"] = serverName
//任务分组 //任务分组

View File

@@ -32,7 +32,7 @@ func AddJob(spec string, job *Job) bool {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
if GetEntryById(job.id) != nil { if GetEntryById(job.jobKey) != nil {
return false return false
} }
err := mainCron.AddJob(spec, job) err := mainCron.AddJob(spec, job)
@@ -40,13 +40,14 @@ func AddJob(spec string, job *Job) bool {
beego.Error("AddJob: ", err.Error()) beego.Error("AddJob: ", err.Error())
return false return false
} }
//fmt.Println(job)
return true return true
} }
func RemoveJob(id int) { func RemoveJob(jobKey int) {
mainCron.RemoveJob(func(e *cron.Entry) bool { mainCron.RemoveJob(func(e *cron.Entry) bool {
if v, ok := e.Job.(*Job); ok { if v, ok := e.Job.(*Job); ok {
if v.id == id { if v.jobKey == jobKey {
return true return true
} }
} }
@@ -54,11 +55,11 @@ func RemoveJob(id int) {
}) })
} }
func GetEntryById(id int) *cron.Entry { func GetEntryById(jobKey int) *cron.Entry {
entries := mainCron.Entries() entries := mainCron.Entries()
for _, e := range entries { for _, e := range entries {
if v, ok := e.Job.(*Job); ok { if v, ok := e.Job.(*Job); ok {
if v.id == id { if v.jobKey == jobKey {
return e return e
} }
} }

View File

@@ -18,12 +18,16 @@ import (
func InitJobs() { func InitJobs() {
list, _ := models.TaskGetList(1, 1000000, "status", 1) list, _ := models.TaskGetList(1, 1000000, "status", 1)
for _, task := range list { for _, task := range list {
job, err := NewJobFromTask(task) jobs, err := NewJobFromTask(task)
if err != nil { if err != nil {
beego.Error("InitJobs:", err.Error()) beego.Error("InitJobs:", err.Error())
continue continue
} }
AddJob(task.CronSpec, job)
for _, job := range jobs {
AddJob(task.CronSpec, job)
}
} }
} }

View File

@@ -20,19 +20,22 @@ import (
"strconv" "strconv"
"strings" "strings"
"encoding/json"
"github.com/astaxie/beego" "github.com/astaxie/beego"
"github.com/axgle/mahonia"
"github.com/george518/PPGo_Job/models" "github.com/george518/PPGo_Job/models"
"github.com/george518/PPGo_Job/notify" "github.com/george518/PPGo_Job/notify"
"golang.org/x/crypto/ssh"
"encoding/json"
"github.com/axgle/mahonia"
"github.com/pkg/errors"
"github.com/linxiaozhi/go-telnet" "github.com/linxiaozhi/go-telnet"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
) )
type Job struct { type Job struct {
id int // 任务ID jobKey int // jobId = id*10000+serverId
id int // taskID
logId int64 // 日志记录ID logId int64 // 日志记录ID
serverId int //服务器信息
serverName string //服务器名称
name string // 任务名称 name string // 任务名称
task *models.Task // 任务对象 task *models.Task // 任务对象
runFunc func(time.Duration) (string, string, error, bool) // 执行函数 runFunc func(time.Duration) (string, string, error, bool) // 执行函数
@@ -40,51 +43,79 @@ type Job struct {
Concurrent bool // 同一个任务是否允许并行执行 Concurrent bool // 同一个任务是否允许并行执行
} }
func NewJobFromTask(task *models.Task) (*Job, error) { func NewJobFromTask(task *models.Task) ([]*Job, error) {
if task.Id < 1 { if task.Id < 1 {
return nil, fmt.Errorf("ToJob: 缺少id") return nil, fmt.Errorf("ToJob: 缺少id")
} }
//本地程序执行 if task.ServerIds == "" {
if task.ServerId == 0 { return nil, fmt.Errorf("任务执行失败,找不到执行的服务器")
job := NewCommandJob(task.Id, task.TaskName, task.Command)
job.task = task
job.Concurrent = task.Concurrent == 1
return job, nil
} }
server, _ := models.TaskServerGetById(task.ServerId) TaskServerIdsArr := strings.Split(task.ServerIds, ",")
if server.ConnectionType == 0 {
if server.Type == 0 {
//密码验证登录服务器
job := RemoteCommandJobByPassword(task.Id, task.TaskName, task.Command, server)
job.task = task
job.Concurrent = task.Concurrent == 1
return job, nil
}
job := RemoteCommandJob(task.Id, task.TaskName, task.Command, server) jobArr := make([]*Job, 0)
job.task = task
job.Concurrent = task.Concurrent == 1 for _, server_id := range TaskServerIdsArr {
return job, nil if server_id == "0" {
} else if server.ConnectionType == 1 { //本地执行
if server.Type == 0 { job := NewCommandJob(task.Id, 0, task.TaskName, task.Command)
//密码验证登录服务器
job := RemoteCommandJobByTelnetPassword(task.Id, task.TaskName, task.Command, server)
job.task = task job.task = task
job.Concurrent = task.Concurrent == 1 job.Concurrent = task.Concurrent == 1
return job, nil job.serverId = 0
job.serverName = "本地服务器"
jobArr = append(jobArr, job)
} else {
server_id_int, _ := strconv.Atoi(server_id)
//远程执行
server, _ := models.TaskServerGetById(server_id_int)
if server.Status == 1 {
fmt.Println("服务器已禁用")
continue
}
if server.ConnectionType == 0 {
if server.Type == 0 {
//密码验证登录服务器
job := RemoteCommandJobByPassword(task.Id, server_id_int, task.TaskName, task.Command, server)
job.task = task
job.Concurrent = task.Concurrent == 1
job.serverId = server_id_int
job.serverName = server.ServerName
jobArr = append(jobArr, job)
} else {
job := RemoteCommandJob(task.Id, server_id_int, task.TaskName, task.Command, server)
job.task = task
job.Concurrent = task.Concurrent == 1
job.serverId = server_id_int
job.serverName = server.ServerName
jobArr = append(jobArr, job)
}
} else if server.ConnectionType == 1 {
if server.Type == 0 {
//密码验证登录服务器
job := RemoteCommandJobByTelnetPassword(task.Id, server_id_int, task.TaskName, task.Command, server)
job.task = task
job.Concurrent = task.Concurrent == 1
job.serverId = server_id_int
job.serverName = server.ServerName
jobArr = append(jobArr, job)
}
}
} }
} }
return nil, fmt.Errorf("未知ConnectionType") return jobArr, nil
} }
func NewCommandJob(id int, name string, command string) *Job { func NewCommandJob(id int, serverId int, name string, command string) *Job {
job := &Job{ job := &Job{
id: id, id: id,
name: name, name: name,
} }
job.jobKey = jobKey(id, serverId)
job.runFunc = func(timeout time.Duration) (string, string, error, bool) { job.runFunc = func(timeout time.Duration) (string, string, error, bool) {
bufOut := new(bytes.Buffer) bufOut := new(bytes.Buffer)
bufErr := new(bytes.Buffer) bufErr := new(bytes.Buffer)
@@ -106,11 +137,15 @@ func NewCommandJob(id int, name string, command string) *Job {
} }
//远程执行任务 密钥验证 //远程执行任务 密钥验证
func RemoteCommandJob(id int, name string, command string, servers *models.TaskServer) *Job { func RemoteCommandJob(id int, serverId int, name string, command string, servers *models.TaskServer) *Job {
job := &Job{ job := &Job{
id: id, id: id,
name: name, name: name,
serverId: serverId,
} }
job.jobKey = jobKey(id, serverId)
job.runFunc = func(timeout time.Duration) (string, string, error, bool) { job.runFunc = func(timeout time.Duration) (string, string, error, bool) {
key, err := ioutil.ReadFile(servers.PrivateKeySrc) key, err := ioutil.ReadFile(servers.PrivateKeySrc)
@@ -166,7 +201,7 @@ func RemoteCommandJob(id int, name string, command string, servers *models.TaskS
return job return job
} }
func RemoteCommandJobByPassword(id int, name string, command string, servers *models.TaskServer) *Job { func RemoteCommandJobByPassword(id int, serverId int, name string, command string, servers *models.TaskServer) *Job {
var ( var (
auth []ssh.AuthMethod auth []ssh.AuthMethod
addr string addr string
@@ -177,9 +212,11 @@ func RemoteCommandJobByPassword(id int, name string, command string, servers *mo
) )
job := &Job{ job := &Job{
id: id, id: id,
name: name, name: name,
serverId: serverId,
} }
job.jobKey = jobKey(id, serverId)
job.runFunc = func(timeout time.Duration) (string, string, error, bool) { job.runFunc = func(timeout time.Duration) (string, string, error, bool) {
// get auth method // get auth method
auth = make([]ssh.AuthMethod, 0) auth = make([]ssh.AuthMethod, 0)
@@ -223,12 +260,15 @@ func RemoteCommandJobByPassword(id int, name string, command string, servers *mo
return job return job
} }
func RemoteCommandJobByTelnetPassword(id int, name string, command string, servers *models.TaskServer) *Job { func RemoteCommandJobByTelnetPassword(id int, serverId int, name string, command string, servers *models.TaskServer) *Job {
job := &Job{ job := &Job{
id: id, id: id,
name: name, name: name,
serverId: serverId,
} }
job.jobKey = jobKey(id, serverId)
job.runFunc = func(timeout time.Duration) (string, string, error, bool) { job.runFunc = func(timeout time.Duration) (string, string, error, bool) {
addr := fmt.Sprintf("%s:%d", servers.ServerIp, servers.Port) addr := fmt.Sprintf("%s:%d", servers.ServerIp, servers.Port)
@@ -309,7 +349,7 @@ func (j *Job) GetLogId() int64 {
func (j *Job) Run() { func (j *Job) Run() {
if !j.Concurrent && j.status > 0 { if !j.Concurrent && j.status > 0 {
beego.Warn(fmt.Sprintf("任务[%d]上一次执行尚未结束,本次被忽略。", j.id)) beego.Warn(fmt.Sprintf("任务[%d]上一次执行尚未结束,本次被忽略。", j.jobKey))
return return
} }
@@ -326,7 +366,7 @@ func (j *Job) Run() {
}() }()
} }
beego.Debug(fmt.Sprintf("开始执行任务: %d", j.id)) beego.Debug(fmt.Sprintf("开始执行任务: %d", j.jobKey))
j.status++ j.status++
defer func() { defer func() {
@@ -344,6 +384,8 @@ func (j *Job) Run() {
// 插入日志 // 插入日志
log := new(models.TaskLog) log := new(models.TaskLog)
log.TaskId = j.id log.TaskId = j.id
log.ServerId = j.serverId
log.ServerName = j.serverName
log.Output = cmdOut log.Output = cmdOut
log.Error = cmdErr log.Error = cmdErr
log.ProcessTime = int(ut) log.ProcessTime = int(ut)
@@ -536,3 +578,8 @@ func gbkAsUtf8(str string) string {
_, resBytes, _ := desDecoder.Translate([]byte(resStr), true) _, resBytes, _ := desDecoder.Translate([]byte(resStr), true)
return string(resBytes) return string(resBytes)
} }
//任务识别码
func jobKey(taskId, serverId int) int {
return taskId*10000 + serverId
}

View File

@@ -9,6 +9,8 @@ package models
import ( import (
"fmt" "fmt"
"strconv"
"strings"
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
) )
@@ -95,6 +97,22 @@ func TaskServerGetById(id int) (*TaskServer, error) {
return obj, nil return obj, nil
} }
//
func TaskServerGetByIds(ids string) ([]*TaskServer, int64) {
serverFilters := make([]interface{}, 0)
//serverFilters = append(serverFilters, "status", 1)
TaskServerIdsArr := strings.Split(ids, ",")
TaskServerIds := make([]int, 0)
for _, v := range TaskServerIdsArr {
id, _ := strconv.Atoi(v)
TaskServerIds = append(TaskServerIds, id)
}
serverFilters = append(serverFilters, "id__in", TaskServerIds)
return TaskServerGetList(1, 1000, serverFilters...)
}
func TaskServerDelById(id int) error { func TaskServerDelById(id int) error {
_, err := orm.NewOrm().QueryTable(TableName("task_server")).Filter("id", id).Delete() _, err := orm.NewOrm().QueryTable(TableName("task_server")).Filter("id", id).Delete()
return err return err

View File

@@ -23,7 +23,7 @@ const (
type Task struct { type Task struct {
Id int Id int
GroupId int GroupId int
ServerId int ServerIds string
TaskName string TaskName string
Description string Description string
CronSpec string CronSpec string

View File

@@ -14,6 +14,8 @@ import (
type TaskLog struct { type TaskLog struct {
Id int Id int
TaskId int TaskId int
ServerId int
ServerName string
Output string Output string
Error string Error string
Status int Status int

View File

@@ -330,4 +330,13 @@ BEGIN;
ALTER TABLE `pp_task_server` ADD `connection_type` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '连接类型 0:SSH;1:Telnet;' AFTER `group_id`; ALTER TABLE `pp_task_server` ADD `connection_type` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '连接类型 0:SSH;1:Telnet;' AFTER `group_id`;
COMMIT; COMMIT;
BEGIN;
ALTER TABLE `ppgo_job2`.`pp_task` CHANGE COLUMN `server_id` `server_ids` varchar(200) NOT NULL DEFAULT '0' COMMENT '服务器id字符串英文都好隔开';
BEGIN;
BEGIN;
ALTER TABLE `ppgo_job2`.`pp_task_log` ADD COLUMN `server_id` int(11) NOT NULL DEFAULT '-1' COMMENT '服务器ID-1异常' AFTER `task_id`, CHANGE COLUMN `output` `output` mediumtext NOT NULL COMMENT '任务输出' AFTER `server_id`, CHANGE COLUMN `error` `error` text NOT NULL COMMENT '错误信息' AFTER `output`, CHANGE COLUMN `status` `status` tinyint(4) NOT NULL COMMENT '状态' AFTER `error`, CHANGE COLUMN `process_time` `process_time` int(11) NOT NULL DEFAULT '0' COMMENT '消耗时间/毫秒' AFTER `status`, CHANGE COLUMN `create_time` `create_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间' AFTER `process_time`;
ALTER TABLE `ppgo_job2`.`pp_task_log` ADD COLUMN `server_name` varchar(60) NOT NULL DEFAULT '\"\"' COMMENT '服务器名称' AFTER `server_id`, CHANGE COLUMN `output` `output` mediumtext NOT NULL COMMENT '任务输出' AFTER `server_name`, CHANGE COLUMN `error` `error` text NOT NULL COMMENT '错误信息' AFTER `output`, CHANGE COLUMN `status` `status` tinyint(4) NOT NULL COMMENT '状态' AFTER `error`, CHANGE COLUMN `process_time` `process_time` int(11) NOT NULL DEFAULT '0' COMMENT '消耗时间/毫秒' AFTER `status`, CHANGE COLUMN `create_time` `create_time` int(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间' AFTER `process_time`;
BEGIN;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -106,7 +106,7 @@ var $;
$.post('{{urlfor "AdminController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "AdminController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () { layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () {
window.location.reload() window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -113,7 +113,7 @@ var $;
$.post('{{urlfor "AdminController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "AdminController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () { layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () {
window.location.reload() window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -89,8 +89,7 @@
$.post('{{urlfor "NotifyTplController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "NotifyTplController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () { layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () {
// window.history.go(-1)// window.parent.deleteCurrentTab();
window.location.reload()
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -89,8 +89,7 @@
$.post('{{urlfor "NotifyTplController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "NotifyTplController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () { layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () {
// window.history.go(-1) window.parent.deleteCurrentTab();
window.location.reload()
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -26,7 +26,7 @@
{{.pageTitle}} {{.pageTitle}}
</div> </div>
<div class="tr fr"> <div class="tr fr">
<button class="layui-btn layui-btn-radius layui-btn-primary layui-btn-xs" onclick="javascript:window.location.reload();"><i class="fa fa-refresh" aria-hidden="true"></i></button> <button class="layui-btn layui-btn-radius layui-btn-primary layui-btn-xs" onclick="javascript:window.location.reload();"><i class="fa fa-refresh" aria-hidden="true" id="reload"></i></button>
</div> </div>
</div> </div>
<!-- 公共头部 end--> <!-- 公共头部 end-->

View File

@@ -106,6 +106,29 @@
return str; return str;
} }
//新页面编辑或者新增,完成后关闭本页并刷新上一页
function deleteCurrentTab(){
//关闭
var lay_id = $(parent.document).find("ul.layui-tab-title").children("li.layui-this").find(".layui-tab-close");
lay_id.click();
//刷新
var currents =$(parent.document).find("div.layui-tab-content").find("div.layui-show").find("iframe").contents();
$(currents[0]).find("body").find("#reload").click();
}
function getCheckboxValue(name)
{
var arr = new Array();
$("input:checkbox[name="+name+"]:checked").each(function(){
console.log($(this).val());
arr.push($(this).val());
});
return arr.join(",");
}
function openTab(url,title,id,icon){ function openTab(url,title,id,icon){
if (!url || !title || !id) { if (!url || !title || !id) {
@@ -235,6 +258,9 @@
layer.tips($(this).attr('data-title'), $(this),{time:1000}); layer.tips($(this).attr('data-title'), $(this),{time:1000});
}); });
element.render(); element.render();
// $("#default_tab").html('<i class="fa fa-home back_space1"></i>系统首页'); // $("#default_tab").html('<i class="fa fa-home back_space1"></i>系统首页');

View File

@@ -156,7 +156,7 @@
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () { layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () {
// window.history.go(-1)// // window.history.go(-1)//
window.location.reload() window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -154,7 +154,7 @@
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () { layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () {
//window.location.href = "/server/list" //window.location.href = "/server/list"
windwo.location.reload(); window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -155,7 +155,9 @@
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () { layer.msg("操作成功",{icon: 1,shade:0.3,time:1000},function () {
// window.history.go(-1) // window.history.go(-1)
window.location.reload() // window.location.reload()
window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -27,19 +27,22 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label mw200">服务器资源</label> <label class="layui-form-label mw200">服务器资源</label>
<div class="layui-inline"> <div class="layui-block">
<div class="layui-col-md3"></div>
<div class="layui-input-inline"> <div class="layui-col-md9">
<select name="server_id"> <div style="margin-bottom: 20px;">
<option value="0">本地服务器</option> <input type="checkbox" name="server_id" lay-skin="primary" title="本地服务器" value="0">
{{range $k, $v := .serverGroup}} </div>
<optgroup label="{{$v.GroupName}}"> <div class="layui-row layui-col-space10">
{{range $kk, $vv := $v.Servers}} {{range $k, $v := .serverGroup}}
<option value="{{$kk}}">{{$vv}}</option> <div class="layui-col-md12" style="background: #efefef;">{{$v.GroupName}}</div>
{{range $kk, $vv := $v.Servers}}
<div class="layui-col-md4">
<input type="checkbox" name="server_id" lay-skin="primary" title="{{$vv}}" value="{{$kk}}">
</div>
{{end}}
{{end}} {{end}}
</optgroup> </div>
{{end}}
</select>
</div> </div>
</div> </div>
</div> </div>
@@ -129,7 +132,7 @@
<input type="checkbox" name="notify_user" lay-filter="notify_user" title="{{$v.RealName}}" <input type="checkbox" name="notify_user" lay-filter="notify_user" title="{{$v.RealName}}"
value="{{$v.Id}}" lay-skin="primary"> value="{{$v.Id}}" lay-skin="primary">
{{end}} {{end}}
<input type="hidden" name="notify_user_ids" id="notify_user_ids" value="">
</div> </div>
<div class="layui-form-mid layui-word-aux"></div> <div class="layui-form-mid layui-word-aux"></div>
</div> </div>
@@ -159,7 +162,7 @@
layer.tips('设为的话如果该任务在上一个时间点还没执行完则略过不执行', '#des', { layer.tips('设为的话如果该任务在上一个时间点还没执行完则略过不执行', '#des', {
tips: [1, '#0FA6D8'] //还可配置颜色 tips: [1, '#0FA6D8'] //还可配置颜色
}); });
}) });
var notify_user_ids = []; var notify_user_ids = [];
@@ -176,6 +179,7 @@
} }
$("#notify_user_ids").val(notify_user_ids.join(",")); $("#notify_user_ids").val(notify_user_ids.join(","));
}); });
form.on('radio(is_notify)', function (data) { form.on('radio(is_notify)', function (data) {
if (data.value == 1) { if (data.value == 1) {
$(".notify").show() $(".notify").show()
@@ -185,12 +189,36 @@
}); });
form.on('submit(sub)', function (data) { form.on('submit(sub)', function (data) {
var form_data = data.field; var form_data = data.field;
var server_arr = new Array();
$("input:checkbox[name=server_id]:checked").each(function(){
server_arr.push($(this).val());
});
form_data.server_ids = server_arr.join(",");
if (form_data.server_ids==="" || form_data.server_ids===null){
layer.msg("请选择服务器资源");
return false;
}
var notify_user_arr = new Array();
$("input:checkbox[name=notify_user]:checked").each(function(){
notify_user_arr.push($(this).val());
});
form_data.notify_user_ids = notify_user_arr.join(",");
if (form_data.is_notify==1 && form_data.notify_user_ids==="") {
layer.msg("请选择通知用户");
return false;
}
$.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () { layer.msg("操作成功", {icon: 1, shade: 0.3, time: 1000}, function () {
// window.history.go(-1) // window.history.go(-1)
window.location.reload(); window.parent.deleteCurrentTab();
}) })
} else { } else {

View File

@@ -175,7 +175,7 @@
if (out.status == 0) { if (out.status == 0) {
layer.msg("复制成功!,请到列表中查看",{icon: 1,shade:0.3,time:1000},function () { layer.msg("复制成功!,请到列表中查看",{icon: 1,shade:0.3,time:1000},function () {
//self.location=document.referrer; //self.location=document.referrer;
window.location.reload(); window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)

View File

@@ -26,20 +26,24 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label mw200">服务器资源</label> <label class="layui-form-label mw200">服务器资源</label>
<div class="layui-inline"> <div class="layui-block">
<div class="layui-col-md3"></div>
<div class="layui-input-inline"> <div class="layui-col-md9">
<select name="server_id"> <div style="margin-bottom: 20px;">
<option value="0">本地服务器</option> <input type="checkbox" name="server_id" lay-skin="primary" title="本地服务器" value="0" {{range $ks,$vs:=$.service_ids}} {{if eq 0 $vs}}checked{{end}}{{end}}>
{{range $k, $v := .serverGroup}} </div>
<optgroup label="{{$v.GroupName}}"> <div class="layui-row layui-col-space10">
{{range $kk, $vv := $v.Servers}} {{range $k, $v := .serverGroup}}
<option value="{{$kk}}" {{if eq $kk $.task.ServerId}}selected{{end}} >{{$vv}}</option> <div class="layui-col-md12" style="background: #efefef;">{{$v.GroupName}}</div>
{{range $kk, $vv := $v.Servers}}
<div class="layui-col-md4">
<input type="checkbox" name="server_id" lay-skin="primary" title="{{$vv}}" value="{{$kk}}" {{range $ks,$vs:=$.service_ids}} {{if eq $kk $vs}}checked{{end}}{{end}}>
</div>
{{end}}
{{end}} {{end}}
</optgroup> </div>
{{end}}
</select>
</div> </div>
</div> </div>
</div> </div>
@@ -182,6 +186,7 @@
$("#notify_user_ids").val(notify_user_ids.join(",")); $("#notify_user_ids").val(notify_user_ids.join(","));
}); });
form.on('submit(sub)', function(data){ form.on('submit(sub)', function(data){
var isAdmin = "{{.isAdmin}}"; var isAdmin = "{{.isAdmin}}";
var msg = "编辑任务需要重新审核,是否确认需要编辑?"; var msg = "编辑任务需要重新审核,是否确认需要编辑?";
@@ -193,15 +198,26 @@
} }
layer.confirm(msg, {icon: 3, title:'提示'}, function(index){ layer.confirm(msg, {icon: 3, title:'提示'}, function(index){
layer.load();
var form_data = data.field; var form_data = data.field;
var ids = [];
$("input[name=server_id][type=checkbox]").each(function() {
if ($(this).prop("checked")) {
ids.push($(this).val());
}
});
if (ids.length<1){
layer.msg("请选择服务资源");
return false;
}
form_data.server_ids = ids.join(",");
$.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) { $.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) {
if (out.status == 0) { if (out.status == 0) {
layer.msg(okmsg,{icon: 1,shade:0.3,time:1000},function () { layer.msg(okmsg,{icon: 1,shade:0.3,time:1000},function () {
// self.location=document.referrer; // self.location=document.referrer;
window.location.reload(); window.parent.deleteCurrentTab();
}) })
} else { } else {
layer.msg(out.message) layer.msg(out.message)
return return

View File

@@ -50,6 +50,12 @@
<td>{{.taskLog.status|str2html}}</td> <td>{{.taskLog.status|str2html}}</td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td>运行服务器</td>
<td>{{.taskLog.server_name}}</td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>

View File

@@ -46,12 +46,13 @@
,url: "/tasklog/table?task_id={{.task_id}}" ,url: "/tasklog/table?task_id={{.task_id}}"
,cols: [[ ,cols: [[
{checkbox: true, fixed: true} {checkbox: true, fixed: true}
,{field:'id', title: 'ID', align:'center',sort: true, width:150} // ,{field:'id', title: 'ID', align:'center',sort: true, width:150}
,{field:'task_id', title: '任务ID', align:'center',sort: true, width:150} ,{field:'task_id', title: '任务ID', align:'center',width:100}
,{field:'server_name', title: '服务器', width:150}
,{field:'start_time',title: '开始时间'} ,{field:'start_time',title: '开始时间'}
,{field:'process_time',width:100, title: '执行时间'} ,{field:'process_time',width:100, title: '执行时间'}
,{field:'output_size',title: '输出'} ,{field:'output_size',title: '输出'}
,{field:'status', width:170,title: '状态'} ,{field:'status', width:100,title: '状态'}
,{fixed: 'right', align:'center', title:'操作', toolbar: '#bar'} ,{fixed: 'right', align:'center', title:'操作', toolbar: '#bar'}
]] ]]
,id: 'listReload' ,id: 'listReload'