mirror of https://github.com/ouqiang/gocron
新增RPC任务执行, 删除SSH任务
parent
604384a5f4
commit
ccad05e50f
|
@ -71,5 +71,5 @@
|
||||||
* UI框架 [Semantic UI](https://semantic-ui.com/)
|
* UI框架 [Semantic UI](https://semantic-ui.com/)
|
||||||
* 依赖管理(所有依赖包放入vendor目录) [govendor](https://github.com/kardianos/govendor)
|
* 依赖管理(所有依赖包放入vendor目录) [govendor](https://github.com/kardianos/govendor)
|
||||||
|
|
||||||
## 贡献
|
## 反馈
|
||||||
欢迎提交PR
|
提交[issue](https://github.com/ouqiang/gocron/issues/new)
|
||||||
|
|
6
build.sh
6
build.sh
|
@ -52,11 +52,11 @@ if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo '开始编译'
|
echo '开始编译调度中心'
|
||||||
if [[ $OS = 'windows' ]];then
|
if [[ $OS = 'windows' ]];then
|
||||||
GOOS=$OS GOARCH=$ARCH go build -ldflags '-w -H windowsgui'
|
GOOS=$OS GOARCH=$ARCH go build -tags gocron -ldflags '-w -H windowsgui'
|
||||||
else
|
else
|
||||||
GOOS=$OS GOARCH=$ARCH go build -ldflags '-w'
|
GOOS=$OS GOARCH=$ARCH go build -tags gocron -ldflags '-w'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $? != 0 ]];then
|
if [[ $? != 0 ]];then
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# set -x -u
|
||||||
|
# 任务节点打包, 生成压缩包 gocron-node.zip或gocron-node.tar.gz
|
||||||
|
# ./build-node.sh -p windows -a amd64
|
||||||
|
# 参数含义
|
||||||
|
# -p 指定平台(windows|linux|darwin)
|
||||||
|
# -a 指定体系架构(amd64|386), 默认amd64
|
||||||
|
|
||||||
|
|
||||||
|
# 目标平台 windows,linux,darwin
|
||||||
|
OS=''
|
||||||
|
# 目标平台架构
|
||||||
|
ARCH=''
|
||||||
|
# 应用名称
|
||||||
|
APP_NAME='gocron-node'
|
||||||
|
# 可执行文件名
|
||||||
|
EXEC_NAME=''
|
||||||
|
# 压缩包名称
|
||||||
|
COMPRESS_FILE=''
|
||||||
|
|
||||||
|
|
||||||
|
# -p 平台 -a 架构
|
||||||
|
while getopts "p:a:" OPT;
|
||||||
|
do
|
||||||
|
case $OPT in
|
||||||
|
p) OS=$OPTARG
|
||||||
|
;;
|
||||||
|
a) ARCH=$OPTARG
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $OS ]];then
|
||||||
|
echo "平台不能为空"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $OS != 'windows' && $OS != 'linux' && $OS != 'darwin' ]];then
|
||||||
|
echo '平台错误,支持的平台 windows linux darmin(osx)'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $ARCH ]];then
|
||||||
|
ARCH='amd64'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
||||||
|
echo 'arch错误,仅支持 386 amd64'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $OS = 'windows' ]];then
|
||||||
|
EXEC_NAME=${APP_NAME}.exe
|
||||||
|
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.zip
|
||||||
|
else
|
||||||
|
EXEC_NAME=${APP_NAME}
|
||||||
|
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo '开始编译任务节点'
|
||||||
|
if [[ $OS = 'windows' ]];then
|
||||||
|
GOOS=$OS GOARCH=$ARCH go build -tags node -ldflags '-w -H windowsgui' -o $EXEC_NAME
|
||||||
|
else
|
||||||
|
GOOS=$OS GOARCH=$ARCH go build -tags node -ldflags '-w' -o $EXEC_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $? != 0 ]];then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo '编译完成'
|
||||||
|
|
||||||
|
if [[ $OS = 'windows' ]];then
|
||||||
|
zip -rq $COMPRESS_FILE $EXEC_NAME
|
||||||
|
else
|
||||||
|
tar czf $COMPRESS_FILE $EXEC_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
rm $EXEC_NAME
|
||||||
|
|
||||||
|
echo '打包完成'
|
||||||
|
echo '生成压缩文件--' $COMPRESS_FILE
|
|
@ -0,0 +1,20 @@
|
||||||
|
// +build node
|
||||||
|
// 任务节点
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ouqiang/gocron/modules/rpc/server"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var addr string
|
||||||
|
if (len(os.Args) < 2) {
|
||||||
|
fmt.Println("usage ./gocron-node addr:port")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
addr = os.Args[1]
|
||||||
|
server.Start(addr)
|
||||||
|
}
|
|
@ -1,10 +1,7 @@
|
||||||
package main
|
// +build gocron
|
||||||
|
// 调度中心
|
||||||
|
|
||||||
/*--------------------------------------------------------
|
package main
|
||||||
定时任务调度
|
|
||||||
兼容Linux crontab时间格式语法,最小粒度可精确到每秒
|
|
||||||
支持通过HTTP、SSH协议执行任务
|
|
||||||
--------------------------------------------------------*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ouqiang/gocron/modules/ssh"
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"github.com/ouqiang/gocron/modules/app"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 主机
|
// 主机
|
||||||
|
@ -16,10 +9,8 @@ type Host struct {
|
||||||
Id int16 `xorm:"smallint pk autoincr"`
|
Id int16 `xorm:"smallint pk autoincr"`
|
||||||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
||||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||||
Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名
|
|
||||||
Port int `xorm:"notnull default 22"` // 主机端口
|
Port int `xorm:"notnull default 22"` // 主机端口
|
||||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
||||||
AuthType ssh.HostAuthType `xorm:"tinyint notnull default 1"` // 认证方式 1: 密码 2: 公钥
|
|
||||||
BaseModel `xorm:"-"`
|
BaseModel `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +25,7 @@ func (host *Host) Create() (insertId int16, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||||
return Db.ID(id).Cols("name,alias,username,port,remark,auth_type").Update(host)
|
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +67,7 @@ func (host *Host) List(params CommonMap) ([]Host, error) {
|
||||||
|
|
||||||
func (host *Host) AllList() ([]Host, error) {
|
func (host *Host) AllList() ([]Host, error) {
|
||||||
list := make([]Host, 0)
|
list := make([]Host, 0)
|
||||||
err := Db.Desc("id").Find(&list)
|
err := Db.Cols("name,port").Desc("id").Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
@ -87,38 +78,6 @@ func (host *Host) Total(params CommonMap) (int64, error) {
|
||||||
return session.Count(host)
|
return session.Count(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) GetPasswordByHost(host string) (string, error) {
|
|
||||||
path := app.DataDir + "/ssh/password/" + host
|
|
||||||
|
|
||||||
|
|
||||||
return h.readFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Host) GetPrivateKeyByHost(host string) (string,error) {
|
|
||||||
path := app.DataDir + "/ssh/private_key/" + host
|
|
||||||
|
|
||||||
return h.readFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) readFile(file string) (string, error) {
|
|
||||||
logger.Debug("认证文件路径: ", file)
|
|
||||||
if !utils.FileExist(file) {
|
|
||||||
return "", errors.New(file + "-认证文件不存在或无权限访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
contentByte, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
content := string(contentByte)
|
|
||||||
content = strings.TrimSpace(content)
|
|
||||||
if content == "" {
|
|
||||||
return "", errors.New("密码为空")
|
|
||||||
}
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析where
|
// 解析where
|
||||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||||
if len(params) == 0 {
|
if len(params) == 0 {
|
||||||
|
|
|
@ -2,17 +2,14 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
"github.com/ouqiang/gocron/modules/ssh"
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TaskProtocol int8
|
type TaskProtocol int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
||||||
TaskSSH // SSH命令
|
TaskRPC // RPC方式执行命令
|
||||||
TaskLocalCommand // 本地命令
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 任务
|
// 任务
|
||||||
|
@ -20,12 +17,12 @@ type Task struct {
|
||||||
Id int `xorm:"int pk autoincr"`
|
Id int `xorm:"int pk autoincr"`
|
||||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||||
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3: 系统命令
|
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:系统命令
|
||||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
||||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
||||||
HostId int16 `xorm:"smallint notnull default 0"` // SSH host id,
|
HostId int16 `xorm:"smallint notnull default 0"` // RPC host id,
|
||||||
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
|
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
|
||||||
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
|
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
|
||||||
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
||||||
|
@ -40,9 +37,7 @@ type TaskHost struct {
|
||||||
Task `xorm:"extends"`
|
Task `xorm:"extends"`
|
||||||
Name string
|
Name string
|
||||||
Port int
|
Port int
|
||||||
Username string
|
|
||||||
Alias string
|
Alias string
|
||||||
AuthType ssh.HostAuthType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (TaskHost) TableName() string {
|
func (TaskHost) TableName() string {
|
||||||
|
@ -69,19 +64,6 @@ func (task *Task) CreateTestTask() {
|
||||||
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
|
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
|
||||||
task.Status = Enabled
|
task.Status = Enabled
|
||||||
task.Create()
|
task.Create()
|
||||||
|
|
||||||
// 系统命令
|
|
||||||
task.Id = 0
|
|
||||||
task.Name = "测试系统命令任务"
|
|
||||||
task.Protocol = TaskLocalCommand
|
|
||||||
task.Spec = "@every 1m"
|
|
||||||
task.Status = Enabled
|
|
||||||
if utils.IsWindows() {
|
|
||||||
task.Command = "dir"
|
|
||||||
} else {
|
|
||||||
task.Command = "ls"
|
|
||||||
}
|
|
||||||
task.Create()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
func (task *Task) UpdateBean(id int) (int64, error) {
|
||||||
|
@ -111,7 +93,7 @@ func (task *Task) Enable(id int) (int64, error) {
|
||||||
// 获取所有激活任务
|
// 获取所有激活任务
|
||||||
func (task *Task) ActiveList() ([]TaskHost, error) {
|
func (task *Task) ActiveList() ([]TaskHost, error) {
|
||||||
list := make([]TaskHost, 0)
|
list := make([]TaskHost, 0)
|
||||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
fields := "t.*, host.alias,host.name,host.port"
|
||||||
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ?", Enabled).Cols(fields).Find(&list)
|
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ?", Enabled).Cols(fields).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
|
@ -120,7 +102,7 @@ func (task *Task) ActiveList() ([]TaskHost, error) {
|
||||||
// 获取某个主机下的所有激活任务
|
// 获取某个主机下的所有激活任务
|
||||||
func (task *Task) ActiveListByHostId(hostId int16) ([]TaskHost, error) {
|
func (task *Task) ActiveListByHostId(hostId int16) ([]TaskHost, error) {
|
||||||
list := make([]TaskHost, 0)
|
list := make([]TaskHost, 0)
|
||||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
fields := "t.*, host.alias,host.name,host.port"
|
||||||
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ? AND t.host_id = ?", Enabled, hostId).Cols(fields).Find(&list)
|
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ? AND t.host_id = ?", Enabled, hostId).Cols(fields).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
|
@ -146,7 +128,7 @@ func (task *Task) NameExist(name string, id int) (bool, error) {
|
||||||
|
|
||||||
func(task *Task) Detail(id int) (TaskHost, error) {
|
func(task *Task) Detail(id int) (TaskHost, error) {
|
||||||
taskHost := TaskHost{}
|
taskHost := TaskHost{}
|
||||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
fields := "t.*, host.alias,host.name,host.port"
|
||||||
_, err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.id=?", id).Cols(fields).Get(&taskHost)
|
_, err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.id=?", id).Cols(fields).Get(&taskHost)
|
||||||
|
|
||||||
return taskHost, err
|
return taskHost, err
|
||||||
|
|
|
@ -14,15 +14,14 @@ type TaskLog struct {
|
||||||
TaskId int `xorm:"int notnull index default 0"` // 任务id
|
TaskId int `xorm:"int notnull index default 0"` // 任务id
|
||||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||||
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3:系统命令
|
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:RPC
|
||||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
|
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
|
||||||
Hostname string `xorm:"varchar(128) notnull defalut '' "` // SSH主机名,逗号分隔
|
Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔
|
||||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
||||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
||||||
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
||||||
NotifyId string `xorm:"varchar(32) notnull default '' "` // 回调通知ID
|
|
||||||
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
|
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
|
||||||
TotalTime int `xorm:"-"` // 执行总时长
|
TotalTime int `xorm:"-"` // 执行总时长
|
||||||
BaseModel `xorm:"-"`
|
BaseModel `xorm:"-"`
|
||||||
|
@ -42,16 +41,6 @@ func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) {
|
||||||
return Db.Table(taskLog).ID(id).Update(data)
|
return Db.Table(taskLog).ID(id).Update(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) UpdateStatus(notifyId string, status Status, result string) (int64, error) {
|
|
||||||
taskLog.Status = status
|
|
||||||
taskLog.Result = result
|
|
||||||
return Db.Cols("status,result").Where("notify_id = ?", notifyId).Update(taskLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (taskLog *TaskLog) setStatus(id int64, status Status) (int64, error) {
|
|
||||||
return taskLog.Update(id, CommonMap{"status": status})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
||||||
taskLog.parsePageAndPageSize(params)
|
taskLog.parsePageAndPageSize(params)
|
||||||
list := make([]TaskLog, 0)
|
list := make([]TaskLog, 0)
|
||||||
|
@ -72,17 +61,6 @@ func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据通知ID获取任务ID
|
|
||||||
func (taskLog *TaskLog) GetTaskIdByNotifyId(notifyId string) (taskId int, err error) {
|
|
||||||
exist, err := Db.Where("notify_id = ?", notifyId).Get(taskLog)
|
|
||||||
if !exist || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
taskId = taskLog.TaskId
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空表
|
// 清空表
|
||||||
func (taskLog *TaskLog) Clear() (int64, error) {
|
func (taskLog *TaskLog) Clear() (int64, error) {
|
||||||
return Db.Where("1=1").Delete(taskLog);
|
return Db.Where("1=1").Delete(taskLog);
|
||||||
|
|
|
@ -2,22 +2,24 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start() {
|
func Exec(ip string, port int, taskReq *pb.TaskRequest) (output string, err error) {
|
||||||
conn, err := grpc.Dial("127.0.0.1:50000", grpc.WithInsecure())
|
addr := fmt.Sprintf("%s:%d", ip, port);
|
||||||
|
conn, err := grpc.Dial(addr, grpc.WithInsecure())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
return
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
c := pb.NewTaskClient(conn)
|
c := pb.NewTaskClient(conn)
|
||||||
req := new(pb.TaskRequest)
|
resp, err := c.Run(context.Background(), taskReq)
|
||||||
resp, err := c.Run(context.Background(), req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
return
|
||||||
}
|
}
|
||||||
grpclog.Println(resp.Name)
|
output = resp.Output
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
// Code generated by protoc-gen-go.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: task.proto
|
// source: task.proto
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package rpc is a generated protocol buffer package.
|
Package rpc is a generated protocol buffer package.
|
||||||
|
@ -35,7 +34,8 @@ var _ = math.Inf
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
type TaskRequest struct {
|
type TaskRequest struct {
|
||||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Command string `protobuf:"bytes,2,opt,name=command" json:"command,omitempty"`
|
||||||
|
Timeout int32 `protobuf:"varint,3,opt,name=timeout" json:"timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TaskRequest) Reset() { *m = TaskRequest{} }
|
func (m *TaskRequest) Reset() { *m = TaskRequest{} }
|
||||||
|
@ -43,15 +43,22 @@ func (m *TaskRequest) String() string { return proto.CompactTextStrin
|
||||||
func (*TaskRequest) ProtoMessage() {}
|
func (*TaskRequest) ProtoMessage() {}
|
||||||
func (*TaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
func (*TaskRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
func (m *TaskRequest) GetName() string {
|
func (m *TaskRequest) GetCommand() string {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Name
|
return m.Command
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *TaskRequest) GetTimeout() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Timeout
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type TaskResponse struct {
|
type TaskResponse struct {
|
||||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Output string `protobuf:"bytes,1,opt,name=output" json:"output,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TaskResponse) Reset() { *m = TaskResponse{} }
|
func (m *TaskResponse) Reset() { *m = TaskResponse{} }
|
||||||
|
@ -59,9 +66,9 @@ func (m *TaskResponse) String() string { return proto.CompactTextStri
|
||||||
func (*TaskResponse) ProtoMessage() {}
|
func (*TaskResponse) ProtoMessage() {}
|
||||||
func (*TaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
func (*TaskResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
func (m *TaskResponse) GetName() string {
|
func (m *TaskResponse) GetOutput() string {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Name
|
return m.Output
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -146,13 +153,15 @@ var _Task_serviceDesc = grpc.ServiceDesc{
|
||||||
func init() { proto.RegisterFile("task.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("task.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 123 bytes of a gzipped FileDescriptorProto
|
// 157 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0x2c, 0xce,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0x2c, 0xce,
|
||||||
0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2e, 0x2a, 0x48, 0x56, 0x52, 0xe4, 0xe2, 0x0e,
|
0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2e, 0x2a, 0x48, 0x56, 0x72, 0xe4, 0xe2, 0x0e,
|
||||||
0x49, 0x2c, 0xce, 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12, 0xe2, 0x62, 0xc9, 0x4b,
|
0x49, 0x2c, 0xce, 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x92, 0xe0, 0x62, 0x4f, 0xce,
|
||||||
0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x95, 0x94, 0xb8, 0x78, 0x20,
|
0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0x91, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x71, 0x41, 0x32,
|
||||||
0x4a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0xb1, 0xa9, 0x31, 0x32, 0xe1, 0x62, 0x01, 0xa9, 0x11,
|
0x25, 0x99, 0xb9, 0xa9, 0xf9, 0xa5, 0x25, 0x12, 0xcc, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x30, 0xae,
|
||||||
0xd2, 0xe1, 0x62, 0x0e, 0x2a, 0xcd, 0x13, 0x12, 0xd0, 0x2b, 0x2a, 0x48, 0xd6, 0x43, 0x32, 0x58,
|
0x92, 0x1a, 0x17, 0x0f, 0xc4, 0x88, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0x21, 0x31, 0x2e, 0xb6,
|
||||||
0x4a, 0x10, 0x49, 0x04, 0x62, 0x8e, 0x12, 0x43, 0x12, 0x1b, 0xd8, 0x21, 0xc6, 0x80, 0x00, 0x00,
|
0xfc, 0xd2, 0x92, 0x82, 0xd2, 0x12, 0x09, 0x46, 0xb0, 0x11, 0x50, 0x9e, 0x91, 0x09, 0x17, 0x0b,
|
||||||
0x00, 0xff, 0xff, 0xf0, 0x04, 0x2a, 0x14, 0x96, 0x00, 0x00, 0x00,
|
0x48, 0x9d, 0x90, 0x0e, 0x17, 0x73, 0x50, 0x69, 0x9e, 0x90, 0x80, 0x5e, 0x51, 0x41, 0xb2, 0x1e,
|
||||||
|
0x92, 0xe5, 0x52, 0x82, 0x48, 0x22, 0x10, 0xb3, 0x94, 0x18, 0x92, 0xd8, 0xc0, 0x8e, 0x35, 0x06,
|
||||||
|
0x04, 0x00, 0x00, 0xff, 0xff, 0x82, 0x08, 0x5d, 0x10, 0xba, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ service Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
message TaskRequest {
|
message TaskRequest {
|
||||||
string name = 1;
|
string command = 2; // 命令
|
||||||
|
int32 timeout = 3; // 任务执行超时时间
|
||||||
}
|
}
|
||||||
|
|
||||||
message TaskResponse {
|
message TaskResponse {
|
||||||
string name = 1;
|
string output = 1; // 命令输出
|
||||||
}
|
}
|
|
@ -6,25 +6,27 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||||
|
"github.com/ouqiang/gocron/modules/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {}
|
type Server struct {}
|
||||||
|
|
||||||
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
||||||
|
output, err := utils.ExecShellWithTimeout(int(req.Timeout), req.Command)
|
||||||
resp := new(pb.TaskResponse)
|
resp := new(pb.TaskResponse)
|
||||||
resp.Name = "gRPC"
|
resp.Output = output
|
||||||
|
|
||||||
return resp, nil
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start() {
|
func Start(addr string) {
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:50000")
|
l, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
grpclog.Fatal(err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
pb.RegisterTaskServer(s, Server{})
|
pb.RegisterTaskServer(s, Server{})
|
||||||
grpclog.Println("listen address ", "127.0.0.1:50000")
|
grpclog.Println("listen address ", addr)
|
||||||
s.Serve(l)
|
s.Serve(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,21 +6,15 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 执行shell命令,可设置执行超时时间
|
// 执行shell命令,可设置执行超时时间
|
||||||
func ExecShellWithTimeout(timeout int, command string, args... string) (string, error) {
|
func ExecShellWithTimeout(timeout int, command string) (string, error) {
|
||||||
cmd := exec.Command(command, args...)
|
cmd := exec.Command("/bin/bash", "-c", command)
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
Setpgid: true,
|
Setpgid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后台运行
|
|
||||||
if timeout == -1 {
|
|
||||||
go cmd.CombinedOutput()
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
// 不限制超时
|
// 不限制超时
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
output ,err := cmd.CombinedOutput()
|
output ,err := cmd.CombinedOutput()
|
||||||
|
@ -36,9 +30,4 @@ func ExecShellWithTimeout(timeout int, command string, args... string) (string,
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
|
||||||
return string(output), err
|
return string(output), err
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化环境变量
|
|
||||||
func FormatEnv(key, value string) string {
|
|
||||||
return fmt.Sprintf("export %s=%s;", key, value)
|
|
||||||
}
|
}
|
|
@ -7,23 +7,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 执行shell命令,可设置执行超时时间
|
// 执行shell命令,可设置执行超时时间
|
||||||
func ExecShellWithTimeout(timeout int, command string, args... string) (string, error) {
|
func ExecShellWithTimeout(timeout int, command string) (string, error) {
|
||||||
cmd := exec.Command(command, args...)
|
cmd := exec.Command("cmd", "/C", command)
|
||||||
// 隐藏cmd窗口
|
// 隐藏cmd窗口
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
// 后台运行
|
|
||||||
if timeout == -1 {
|
|
||||||
go cmd.CombinedOutput()
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
// 不限制超时
|
// 不限制超时
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
output ,err := cmd.CombinedOutput()
|
output ,err := cmd.CombinedOutput()
|
||||||
return string(output), err
|
|
||||||
|
return ConvertEncoding(string(output), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := time.Duration(timeout) * time.Second
|
d := time.Duration(timeout) * time.Second
|
||||||
|
@ -35,10 +30,15 @@ func ExecShellWithTimeout(timeout int, command string, args... string) (string,
|
||||||
output ,err := cmd.CombinedOutput()
|
output ,err := cmd.CombinedOutput()
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
|
||||||
return string(output), err
|
return ConvertEncoding(string(output), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化环境变量
|
func ConvertEncoding(outputGBK string, err error) (string, error) {
|
||||||
func FormatEnv(key, value string) string {
|
// windows平台编码为gbk,需转换为utf8才能入库
|
||||||
return fmt.Sprintf("set %s=%s & ", key, value)
|
outputUTF8, ok := GBK2UTF8(outputGBK)
|
||||||
|
if ok {
|
||||||
|
return outputUTF8, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "命令输出转换编码失败(gbk to utf8)", err
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
"github.com/ouqiang/gocron/modules/utils"
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
"github.com/ouqiang/gocron/modules/logger"
|
||||||
"strconv"
|
"strconv"
|
||||||
"github.com/ouqiang/gocron/modules/ssh"
|
|
||||||
"github.com/ouqiang/gocron/service"
|
"github.com/ouqiang/gocron/service"
|
||||||
"github.com/Unknwon/paginater"
|
"github.com/Unknwon/paginater"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -56,52 +55,11 @@ func Edit(ctx *macaron.Context) {
|
||||||
ctx.HTML(200, "host/host_form")
|
ctx.HTML(200, "host/host_form")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ping(ctx *macaron.Context) string {
|
|
||||||
id := ctx.ParamsInt(":id")
|
|
||||||
hostModel := new(models.Host)
|
|
||||||
err := hostModel.Find(id)
|
|
||||||
json := utils.JsonResponse{}
|
|
||||||
if err != nil || hostModel.Id <= 0{
|
|
||||||
return json.CommonFailure("主机不存在", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sshConfig := ssh.SSHConfig{}
|
|
||||||
sshConfig.User = hostModel.Username
|
|
||||||
sshConfig.Host = hostModel.Name
|
|
||||||
sshConfig.Port = hostModel.Port
|
|
||||||
sshConfig.ExecTimeout = 5
|
|
||||||
sshConfig.AuthType = hostModel.AuthType
|
|
||||||
var password string
|
|
||||||
var privateKey string
|
|
||||||
if hostModel.AuthType == ssh.HostPassword {
|
|
||||||
password, err = hostModel.GetPasswordByHost(hostModel.Name)
|
|
||||||
if err != nil {
|
|
||||||
return json.CommonFailure(err.Error(), err)
|
|
||||||
}
|
|
||||||
sshConfig.Password = password
|
|
||||||
} else {
|
|
||||||
privateKey, err = hostModel.GetPrivateKeyByHost(hostModel.Name)
|
|
||||||
if err != nil {
|
|
||||||
return json.CommonFailure(err.Error(), err)
|
|
||||||
}
|
|
||||||
sshConfig.PrivateKey = privateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ssh.Exec(sshConfig, "pwd")
|
|
||||||
if err != nil {
|
|
||||||
return json.CommonFailure("连接失败-" + err.Error(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Success("连接成功", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type HostForm struct {
|
type HostForm struct {
|
||||||
Id int16
|
Id int16
|
||||||
Name string `binding:"Required;MaxSize(64)"`
|
Name string `binding:"Required;MaxSize(64)"`
|
||||||
Alias string `binding:"Required;MaxSize(32)"`
|
Alias string `binding:"Required;MaxSize(32)"`
|
||||||
Username string `binding:"Required;MaxSize(32)"`
|
|
||||||
Port int `binding:"Required;Range(1-65535)"`
|
Port int `binding:"Required;Range(1-65535)"`
|
||||||
AuthType ssh.HostAuthType `binding:"Required:Range(1,2)"`
|
|
||||||
Remark string
|
Remark string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +87,8 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
|
|
||||||
hostModel.Name = form.Name
|
hostModel.Name = form.Name
|
||||||
hostModel.Alias = form.Alias
|
hostModel.Alias = form.Alias
|
||||||
hostModel.Username = form.Username
|
|
||||||
hostModel.Port = form.Port
|
hostModel.Port = form.Port
|
||||||
hostModel.Remark = form.Remark
|
hostModel.Remark = form.Remark
|
||||||
hostModel.AuthType = form.AuthType
|
|
||||||
isCreate := false
|
isCreate := false
|
||||||
if id > 0 {
|
if id > 0 {
|
||||||
_, err = hostModel.UpdateBean(id)
|
_, err = hostModel.UpdateBean(id)
|
||||||
|
|
|
@ -69,7 +69,6 @@ func Register(m *macaron.Macaron) {
|
||||||
m.Group("/host", func() {
|
m.Group("/host", func() {
|
||||||
m.Get("/create", host.Create)
|
m.Get("/create", host.Create)
|
||||||
m.Get("/edit/:id", host.Edit)
|
m.Get("/edit/:id", host.Edit)
|
||||||
m.Get("/ping/:id", host.Ping)
|
|
||||||
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
||||||
m.Get("", host.Index)
|
m.Get("", host.Index)
|
||||||
m.Post("/remove/:id", host.Remove)
|
m.Post("/remove/:id", host.Remove)
|
||||||
|
@ -97,7 +96,6 @@ func Register(m *macaron.Macaron) {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
m.Group("/api/v1", func() {
|
m.Group("/api/v1", func() {
|
||||||
m.Route("/tasklog/update-status", "GET,POST", tasklog.UpdateStatus)
|
|
||||||
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
||||||
m.Post("/delaytask/push", delaytask.Create)
|
m.Post("/delaytask/push", delaytask.Create)
|
||||||
m.Post("/delaytask/log/remove/:id", delaytask.Remove)
|
m.Post("/delaytask/log/remove/:id", delaytask.Remove)
|
||||||
|
|
|
@ -20,9 +20,9 @@ type TaskForm struct {
|
||||||
Id int
|
Id int
|
||||||
Name string `binding:"Required;MaxSize(32)"`
|
Name string `binding:"Required;MaxSize(32)"`
|
||||||
Spec string `binding:"Required;MaxSize(64)"`
|
Spec string `binding:"Required;MaxSize(64)"`
|
||||||
Protocol models.TaskProtocol `binding:"In(1,2,3)"`
|
Protocol models.TaskProtocol `binding:"In(1,2)"`
|
||||||
Command string `binding:"Required;MaxSize(256)"`
|
Command string `binding:"Required;MaxSize(256)"`
|
||||||
Timeout int `binding:"Range(-1,86400)"`
|
Timeout int `binding:"Range(0,86400)"`
|
||||||
Multi int8 `binding:"In(1,2)"`
|
Multi int8 `binding:"In(1,2)"`
|
||||||
RetryTimes int8
|
RetryTimes int8
|
||||||
HostId int16
|
HostId int16
|
||||||
|
@ -111,11 +111,11 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
return json.CommonFailure("任务名称已存在")
|
return json.CommonFailure("任务名称已存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Protocol == models.TaskSSH && form.HostId <= 0 {
|
if form.Protocol == models.TaskRPC && form.HostId <= 0 {
|
||||||
return json.CommonFailure("请选择主机名")
|
return json.CommonFailure("请选择主机名")
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Protocol != models.TaskHTTP {
|
if form.Protocol == models.TaskRPC {
|
||||||
taskModel.HostId = form.HostId
|
taskModel.HostId = form.HostId
|
||||||
} else {
|
} else {
|
||||||
taskModel.HostId = 0
|
taskModel.HostId = 0
|
||||||
|
@ -142,9 +142,6 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
|
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
|
||||||
return json.CommonFailure("请输入正确的URL地址")
|
return json.CommonFailure("请输入正确的URL地址")
|
||||||
}
|
}
|
||||||
if taskModel.Timeout == -1 {
|
|
||||||
return json.CommonFailure("HTTP任务不支持后台运行")
|
|
||||||
}
|
|
||||||
if taskModel.Timeout > 300 {
|
if taskModel.Timeout > 300 {
|
||||||
return json.CommonFailure("HTTP任务超时时间不能超过300秒")
|
return json.CommonFailure("HTTP任务超时时间不能超过300秒")
|
||||||
}
|
}
|
||||||
|
@ -155,10 +152,6 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if taskModel.Protocol != models.TaskSSH {
|
|
||||||
taskModel.HostId = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
id, err = taskModel.Create()
|
id, err = taskModel.Create()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,8 +11,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"github.com/ouqiang/gocron/routers/base"
|
"github.com/ouqiang/gocron/routers/base"
|
||||||
"github.com/ouqiang/gocron/service"
|
|
||||||
"errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Index(ctx *macaron.Context) {
|
func Index(ctx *macaron.Context) {
|
||||||
|
@ -66,51 +64,6 @@ func Remove(ctx *macaron.Context) string {
|
||||||
return json.Success("删除成功", nil)
|
return json.Success("删除成功", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新任务状态
|
|
||||||
func UpdateStatus(ctx *macaron.Context) string {
|
|
||||||
id := ctx.QueryTrim("id")
|
|
||||||
status := ctx.QueryInt("status")
|
|
||||||
result := ctx.QueryTrim("result")
|
|
||||||
json := utils.JsonResponse{}
|
|
||||||
|
|
||||||
if id == "" {
|
|
||||||
return json.CommonFailure("任务ID不能为空")
|
|
||||||
}
|
|
||||||
if status != 1 && status != 2 {
|
|
||||||
return json.CommonFailure("status值错误")
|
|
||||||
}
|
|
||||||
if status == 1 {
|
|
||||||
status -= 1
|
|
||||||
}
|
|
||||||
taskLogModel := new(models.TaskLog)
|
|
||||||
affectRows, err := taskLogModel.UpdateStatus(id, models.Status(status), result)
|
|
||||||
if err != nil || affectRows == 0 {
|
|
||||||
return json.CommonFailure("更新任务状态失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送通知
|
|
||||||
taskId, err := taskLogModel.GetTaskIdByNotifyId(id)
|
|
||||||
if err != nil || taskId <= 0 {
|
|
||||||
logger.Error("异步任务回调#根据notify-id获取taskId失败", err)
|
|
||||||
return json.Success("success", nil)
|
|
||||||
}
|
|
||||||
taskModel := new(models.Task)
|
|
||||||
task, err := taskModel.Detail(taskId)
|
|
||||||
if err != nil || task.Id <= 0 {
|
|
||||||
logger.Error("异步任务回调#根据获取任务详情失败", err)
|
|
||||||
return json.Success("success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
taskResult := service.TaskResult{}
|
|
||||||
taskResult.Result = result
|
|
||||||
if status == 0 {
|
|
||||||
taskResult.Err = errors.New("error")
|
|
||||||
}
|
|
||||||
service.SendNotification(task, taskResult)
|
|
||||||
|
|
||||||
return json.Success("success", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析查询参数
|
// 解析查询参数
|
||||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||||
var params models.CommonMap = models.CommonMap{}
|
var params models.CommonMap = models.CommonMap{}
|
||||||
|
|
120
service/task.go
120
service/task.go
|
@ -5,14 +5,14 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
"github.com/ouqiang/gocron/modules/logger"
|
||||||
"github.com/ouqiang/gocron/modules/ssh"
|
|
||||||
"github.com/jakecoffman/cron"
|
"github.com/jakecoffman/cron"
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ouqiang/gocron/modules/httpclient"
|
"github.com/ouqiang/gocron/modules/httpclient"
|
||||||
"github.com/ouqiang/gocron/modules/notify"
|
"github.com/ouqiang/gocron/modules/notify"
|
||||||
"sync"
|
"sync"
|
||||||
|
rpcClient "github.com/ouqiang/gocron/modules/rpc/client"
|
||||||
|
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 定时任务调度管理器
|
// 定时任务调度管理器
|
||||||
|
@ -76,7 +76,6 @@ type TaskResult struct {
|
||||||
Result string
|
Result string
|
||||||
Err error
|
Err error
|
||||||
RetryTimes int8
|
RetryTimes int8
|
||||||
IsAsync bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
||||||
|
@ -137,38 +136,6 @@ type Handler interface {
|
||||||
Run(taskModel models.TaskHost) (string, error)
|
Run(taskModel models.TaskHost) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 本地命令
|
|
||||||
type LocalCommandHandler struct {}
|
|
||||||
|
|
||||||
// 运行本地命令
|
|
||||||
func (h *LocalCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
|
||||||
if taskModel.Command == "" {
|
|
||||||
return "", errors.New("invalid command")
|
|
||||||
}
|
|
||||||
|
|
||||||
if utils.IsWindows() {
|
|
||||||
return h.runOnWindows(taskModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.runOnUnix(taskModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行Windows命令
|
|
||||||
func (h *LocalCommandHandler) runOnWindows(taskModel models.TaskHost) (string, error) {
|
|
||||||
outputGBK, err := utils.ExecShellWithTimeout(taskModel.Timeout, "cmd", "/C", taskModel.Command)
|
|
||||||
// windows平台编码为gbk,需转换为utf8才能入库
|
|
||||||
outputUTF8, ok := utils.GBK2UTF8(outputGBK)
|
|
||||||
if ok {
|
|
||||||
return outputUTF8, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return "命令输出转换编码失败(gbk to utf8)", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行Unix命令
|
|
||||||
func (h *LocalCommandHandler) runOnUnix(taskModel models.TaskHost) (string, error) {
|
|
||||||
return utils.ExecShellWithTimeout(taskModel.Timeout, "/bin/bash", "-c", taskModel.Command)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP任务
|
// HTTP任务
|
||||||
type HTTPHandler struct{}
|
type HTTPHandler struct{}
|
||||||
|
@ -189,42 +156,20 @@ func (h *HTTPHandler) Run(taskModel models.TaskHost) (result string, err error)
|
||||||
return resp.Body, err
|
return resp.Body, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH-command任务
|
// RPC调用执行任务
|
||||||
type SSHCommandHandler struct{}
|
type RPCHandler struct {}
|
||||||
|
|
||||||
func (h *SSHCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
func (h *RPCHandler) Run(taskModel models.TaskHost) (result string, err error) {
|
||||||
hostModel := new(models.Host)
|
taskRequest := new(pb.TaskRequest)
|
||||||
err := hostModel.Find(int(taskModel.HostId))
|
taskRequest.Timeout = int32(taskModel.Timeout)
|
||||||
if err != nil {
|
taskRequest.Command = taskModel.Command
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
sshConfig := ssh.SSHConfig{}
|
|
||||||
sshConfig.User = hostModel.Username
|
|
||||||
sshConfig.Host = hostModel.Name
|
|
||||||
sshConfig.Port = hostModel.Port
|
|
||||||
sshConfig.ExecTimeout = taskModel.Timeout
|
|
||||||
sshConfig.AuthType = hostModel.AuthType
|
|
||||||
var password string
|
|
||||||
var privateKey string
|
|
||||||
if hostModel.AuthType == ssh.HostPassword {
|
|
||||||
password, err = hostModel.GetPasswordByHost(hostModel.Name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
sshConfig.Password = password
|
|
||||||
} else {
|
|
||||||
privateKey, err = hostModel.GetPrivateKeyByHost(hostModel.Name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
sshConfig.PrivateKey = privateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
return ssh.Exec(sshConfig, taskModel.Command)
|
return rpcClient.Exec(taskModel.Name, taskModel.Port, taskRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 创建任务日志
|
// 创建任务日志
|
||||||
func createTaskLog(taskModel models.TaskHost, status models.Status) (int64, string, error) {
|
func createTaskLog(taskModel models.TaskHost, status models.Status) (int64, error) {
|
||||||
taskLogModel := new(models.TaskLog)
|
taskLogModel := new(models.TaskLog)
|
||||||
taskLogModel.TaskId = taskModel.Id
|
taskLogModel.TaskId = taskModel.Id
|
||||||
taskLogModel.Name = taskModel.Task.Name
|
taskLogModel.Name = taskModel.Task.Name
|
||||||
|
@ -232,20 +177,14 @@ func createTaskLog(taskModel models.TaskHost, status models.Status) (int64, stri
|
||||||
taskLogModel.Protocol = taskModel.Protocol
|
taskLogModel.Protocol = taskModel.Protocol
|
||||||
taskLogModel.Command = taskModel.Command
|
taskLogModel.Command = taskModel.Command
|
||||||
taskLogModel.Timeout = taskModel.Timeout
|
taskLogModel.Timeout = taskModel.Timeout
|
||||||
if taskModel.Protocol == models.TaskSSH {
|
if taskModel.Protocol == models.TaskRPC {
|
||||||
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
|
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
|
||||||
}
|
}
|
||||||
taskLogModel.StartTime = time.Now()
|
taskLogModel.StartTime = time.Now()
|
||||||
taskLogModel.Status = status
|
taskLogModel.Status = status
|
||||||
// SSH执行远程命令,后台运行
|
|
||||||
var notifyId string = ""
|
|
||||||
if taskModel.Timeout == -1 {
|
|
||||||
notifyId = utils.RandString(32);
|
|
||||||
taskLogModel.NotifyId = notifyId;
|
|
||||||
}
|
|
||||||
insertId, err := taskLogModel.Create()
|
insertId, err := taskLogModel.Create()
|
||||||
|
|
||||||
return insertId, notifyId, err
|
return insertId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新任务日志
|
// 更新任务日志
|
||||||
|
@ -255,9 +194,7 @@ func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) {
|
||||||
var result string = taskResult.Result
|
var result string = taskResult.Result
|
||||||
if taskResult.Err != nil {
|
if taskResult.Err != nil {
|
||||||
status = models.Failure
|
status = models.Failure
|
||||||
} else if taskResult.IsAsync {
|
} else {
|
||||||
status = models.Background
|
|
||||||
} else {
|
|
||||||
status = models.Finish
|
status = models.Finish
|
||||||
}
|
}
|
||||||
return taskLogModel.Update(taskLogId, models.CommonMap{
|
return taskLogModel.Update(taskLogId, models.CommonMap{
|
||||||
|
@ -276,7 +213,7 @@ func createJob(taskModel models.TaskHost) cron.FuncJob {
|
||||||
taskFunc := func() {
|
taskFunc := func() {
|
||||||
TaskNum.Add()
|
TaskNum.Add()
|
||||||
defer TaskNum.Done()
|
defer TaskNum.Done()
|
||||||
taskLogId := beforeExecJob(&taskModel)
|
taskLogId := beforeExecJob(taskModel)
|
||||||
if taskLogId <= 0 {
|
if taskLogId <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -294,37 +231,26 @@ func createHandler(taskModel models.TaskHost) Handler {
|
||||||
switch taskModel.Protocol {
|
switch taskModel.Protocol {
|
||||||
case models.TaskHTTP:
|
case models.TaskHTTP:
|
||||||
handler = new(HTTPHandler)
|
handler = new(HTTPHandler)
|
||||||
case models.TaskSSH:
|
case models.TaskRPC:
|
||||||
handler = new(SSHCommandHandler)
|
handler = new(RPCHandler)
|
||||||
case models.TaskLocalCommand:
|
|
||||||
handler = new(LocalCommandHandler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
func beforeExecJob(taskModel *models.TaskHost) (taskLogId int64) {
|
func beforeExecJob(taskModel models.TaskHost) (taskLogId int64) {
|
||||||
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
||||||
createTaskLog(*taskModel, models.Cancel)
|
createTaskLog(taskModel, models.Cancel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if taskModel.Multi == 0 {
|
if taskModel.Multi == 0 {
|
||||||
runInstance.add(taskModel.Id)
|
runInstance.add(taskModel.Id)
|
||||||
}
|
}
|
||||||
taskLogId, notifyId, err := createTaskLog(*taskModel, models.Running)
|
taskLogId, err := createTaskLog(taskModel, models.Running)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("任务开始执行#写入任务日志失败-", err)
|
logger.Error("任务开始执行#写入任务日志失败-", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 设置notifyId到环境变量中
|
|
||||||
if notifyId != "" {
|
|
||||||
envName := "GOCRON_TASK_ID"
|
|
||||||
if taskModel.Protocol == models.TaskSSH {
|
|
||||||
taskModel.Command = fmt.Sprintf("%s%s", utils.FormatUnixEnv(envName, notifyId), taskModel.Command)
|
|
||||||
} else {
|
|
||||||
taskModel.Command = fmt.Sprintf("%s%s", utils.FormatEnv(envName, notifyId), taskModel.Command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf("任务命令-%s", taskModel.Command)
|
logger.Debugf("任务命令-%s", taskModel.Command)
|
||||||
|
|
||||||
|
@ -335,16 +261,10 @@ func afterExecJob(taskModel models.TaskHost, taskResult TaskResult, taskLogId in
|
||||||
if taskResult.Err != nil {
|
if taskResult.Err != nil {
|
||||||
taskResult.Result = taskResult.Err.Error() + "\n" + taskResult.Result
|
taskResult.Result = taskResult.Err.Error() + "\n" + taskResult.Result
|
||||||
}
|
}
|
||||||
if taskModel.Timeout == -1 {
|
|
||||||
taskResult.IsAsync = true
|
|
||||||
}
|
|
||||||
_, err := updateTaskLog(taskLogId, taskResult)
|
_, err := updateTaskLog(taskLogId, taskResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("任务结束#更新任务日志失败-", err)
|
logger.Error("任务结束#更新任务日志失败-", err)
|
||||||
}
|
}
|
||||||
if taskResult.IsAsync {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
SendNotification(taskModel, taskResult)
|
SendNotification(taskModel, taskResult)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,15 @@
|
||||||
<input type="hidden" name="id" value="{{{.Host.Id}}}">
|
<input type="hidden" name="id" value="{{{.Host.Id}}}">
|
||||||
<div class="four fields">
|
<div class="four fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>主机名 (域名或IP)</label>
|
<label>主机名</label>
|
||||||
<div class="ui small input">
|
<div class="ui small input">
|
||||||
<input type="text" name="name" value="{{{.Host.Name}}}">
|
<input type="text" name="name" value="{{{.Host.Name}}}" placeholder="192.168.50.154">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>端口</label>
|
||||||
|
<div class="ui small input">
|
||||||
|
<input type="text" name="port" value="{{{.Host.Port}}}" placeholder="5921">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -29,35 +35,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="four fields">
|
|
||||||
<div class="field">
|
|
||||||
<label>SSH用户名</label>
|
|
||||||
<div class="ui small input">
|
|
||||||
<input type="text" name="username" value="{{{.Host.Username}}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label>SSH端口</label>
|
|
||||||
<div class="ui small input">
|
|
||||||
<input type="text" name="port" value="{{{.Host.Port}}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fields">
|
|
||||||
<div class="field">
|
|
||||||
<div class="ui blue message">
|
|
||||||
密码认证: 把密码写入文件 gocron根目录/data/ssh/password/主机名<br>
|
|
||||||
例: echo '12345678' > data/ssh/password/127.0.0.1<br><br>
|
|
||||||
公钥认证: 把私钥写入文件 gocron根目录/data/ssh/private_key/主机名<br>
|
|
||||||
例: cp ~/.ssh/id_rsa data/ssh/private_key/127.0.0.1
|
|
||||||
</div>
|
|
||||||
<label>认证方式</label>
|
|
||||||
<select name="auth_type" id="authType">
|
|
||||||
<option value="2" {{{if .Host}}} {{{if eq .Host.AuthType 2}}}selected {{{end}}} {{{end}}} data-validate-type="selectPrivateKey" data-match="private_key">公钥</option>
|
|
||||||
<option value="1" {{{if .Host}}} {{{if eq .Host.AuthType 1}}}selected {{{end}}} {{{end}}} data-validate-type="selectPassword" data-match="password">密码</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="two fields">
|
<div class="two fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>备注</label>
|
<label>备注</label>
|
||||||
|
@ -112,19 +89,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
username: {
|
|
||||||
identifier : 'username',
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
type : 'empty',
|
|
||||||
prompt : '请输入SSH用户名'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type : 'maxLength[32]',
|
|
||||||
prompt : '长度不能超过32'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
port: {
|
port: {
|
||||||
identifier : 'port',
|
identifier : 'port',
|
||||||
rules: [
|
rules: [
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>主机名</th>
|
<th>主机名</th>
|
||||||
<th>别名</th>
|
<th>别名</th>
|
||||||
<th>用户名</th>
|
|
||||||
<th>端口</th>
|
<th>端口</th>
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
|
@ -47,7 +46,6 @@
|
||||||
<td>{{{.Id}}}</td>
|
<td>{{{.Id}}}</td>
|
||||||
<td>{{{.Name}}}</td>
|
<td>{{{.Name}}}</td>
|
||||||
<td>{{{.Alias}}}</td>
|
<td>{{{.Alias}}}</td>
|
||||||
<td>{{{.Username}}}</td>
|
|
||||||
<td>{{{.Port}}}</td>
|
<td>{{{.Port}}}</td>
|
||||||
<td>{{{.Remark}}}</td>
|
<td>{{{.Remark}}}</td>
|
||||||
<td class="operation">
|
<td class="operation">
|
||||||
|
@ -55,7 +53,6 @@
|
||||||
<button class="ui positive button" onclick="util.removeConfirm('/host/remove/{{{.Id}}}')">删除</button><br>
|
<button class="ui positive button" onclick="util.removeConfirm('/host/remove/{{{.Id}}}')">删除</button><br>
|
||||||
<div style="margin-top: 5px;">
|
<div style="margin-top: 5px;">
|
||||||
<a class="ui twitter button" href="/task?host_id={{{.Id}}}">查看任务</a>
|
<a class="ui twitter button" href="/task?host_id={{{.Id}}}">查看任务</a>
|
||||||
<button class="ui blue button" @click="ping({{{.Id}}})">连接测试</button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -66,23 +63,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var Vue = new Vue({
|
|
||||||
el: '.ui.striped.table',
|
|
||||||
methods: {
|
|
||||||
ping: function(id) {
|
|
||||||
swal({
|
|
||||||
title: '',
|
|
||||||
text: "连接中.......",
|
|
||||||
type: 'info',
|
|
||||||
showConfirmButton: false
|
|
||||||
});
|
|
||||||
util.get("/host/ping/" + id, function(code, message) {
|
|
||||||
swal('操作成功', '连接成功', 'success');
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{{ template "common/footer" . }}}
|
{{{ template "common/footer" . }}}
|
|
@ -33,8 +33,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<select name="protocol" id="protocol">
|
<select name="protocol" id="protocol">
|
||||||
<option value="0">执行方式</option>
|
<option value="0">执行方式</option>
|
||||||
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>系统命令</option>
|
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">RPC</option>
|
||||||
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
|
||||||
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,7 +70,7 @@
|
||||||
<td>{{{.Id}}}</td>
|
<td>{{{.Id}}}</td>
|
||||||
<td>{{{.Task.Name}}}</td>
|
<td>{{{.Task.Name}}}</td>
|
||||||
<td>{{{.Spec}}}</td>
|
<td>{{{.Spec}}}</td>
|
||||||
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else if eq .Protocol 3}}}本地命令{{{end}}}</td>
|
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} RPC {{{end}}}</td>
|
||||||
<td>{{{if eq .Timeout -1}}}后台运行{{{else if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
|
<td>{{{if eq .Timeout -1}}}后台运行{{{else if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
|
||||||
<td>{{{.RetryTimes}}}</td>
|
<td>{{{.RetryTimes}}}</td>
|
||||||
<td>{{{if gt .Multi 0}}}否{{{else}}}是{{{end}}}</td>
|
<td>{{{if gt .Multi 0}}}否{{{else}}}是{{{end}}}</td>
|
||||||
|
|
|
@ -30,8 +30,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<select name="protocol" id="protocol">
|
<select name="protocol" id="protocol">
|
||||||
<option value="0">执行方式</option>
|
<option value="0">执行方式</option>
|
||||||
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>系统命令</option>
|
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">RPC</option>
|
||||||
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
|
||||||
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,7 +70,7 @@
|
||||||
<td><a href="/task?id={{{.TaskId}}}">{{{.TaskId}}}</a></td>
|
<td><a href="/task?id={{{.TaskId}}}">{{{.TaskId}}}</a></td>
|
||||||
<td>{{{.Name}}}</td>
|
<td>{{{.Name}}}</td>
|
||||||
<td>{{{.Spec}}}</td>
|
<td>{{{.Spec}}}</td>
|
||||||
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else}}} 系统命令 {{{end}}}</td>
|
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} RPC {{{end}}}</td>
|
||||||
<td>{{{if eq .Timeout -1}}}后台运行{{{else if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
|
<td>{{{if eq .Timeout -1}}}后台运行{{{else if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
|
||||||
<td>{{{.RetryTimes}}}</td>
|
<td>{{{.RetryTimes}}}</td>
|
||||||
<td>{{{.Hostname}}}</td>
|
<td>{{{.Hostname}}}</td>
|
||||||
|
|
|
@ -39,8 +39,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>执行方式</label>
|
<label>执行方式</label>
|
||||||
<select name="protocol" id="protocol">
|
<select name="protocol" id="protocol">
|
||||||
<option value="3" {{{if .Task}}} {{{if eq .Task.Protocol 3}}}selected{{{end}}} {{{end}}}>系统命令</option>
|
<option value="2" {{{if .Task}}} {{{if eq .Task.Protocol 2}}}selected{{{end}}} {{{end}}} data-match="host_id" data-validate-type="selectProtocol">RPC</option>
|
||||||
<option value="2" {{{if .Task}}} {{{if eq .Task.Protocol 2}}}selected{{{end}}} {{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
|
||||||
<option value="1" {{{if .Task}}} {{{if eq .Task.Protocol 1}}}selected{{{end}}} {{{end}}}>HTTP</option>
|
<option value="1" {{{if .Task}}} {{{if eq .Task.Protocol 1}}}selected{{{end}}} {{{end}}}>HTTP</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +48,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>主机</label>
|
<label>主机</label>
|
||||||
<div class="ui blue message">
|
<div class="ui blue message">
|
||||||
<pre>选择SSH协议时,需选择执行主机</pre>
|
<pre>选择RPC协议时,需选择执行主机</pre>
|
||||||
</div>
|
</div>
|
||||||
<select name="host_id" id="hostId">
|
<select name="host_id" id="hostId">
|
||||||
<option value="">选择主机</option>
|
<option value="">选择主机</option>
|
||||||
|
@ -183,9 +182,6 @@
|
||||||
case '2':
|
case '2':
|
||||||
$('#command').attr('placeholder', '请输入shell命令');
|
$('#command').attr('placeholder', '请输入shell命令');
|
||||||
break;
|
break;
|
||||||
case '3':
|
|
||||||
$('#command').attr('placeholder', '请输入系统命令');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@ do
|
||||||
if [[ $? != 0 ]];then
|
if [[ $? != 0 ]];then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
./build_node.sh -p $i
|
||||||
|
if [[ $? != 0 ]];then
|
||||||
|
break
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# 身份认证
|
# 身份认证
|
||||||
|
|
Loading…
Reference in New Issue