mirror of https://github.com/ouqiang/gocron
完善主机编辑
parent
f57561118d
commit
0a9e71cb5a
|
@ -99,6 +99,7 @@ func catchSignal() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 作为守护进程运行
|
||||||
func becomeDaemon(ctx *cli.Context) {
|
func becomeDaemon(ctx *cli.Context) {
|
||||||
// 不支持windows
|
// 不支持windows
|
||||||
if app.IsWindows {
|
if app.IsWindows {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "github.com/ouqiang/gocron/modules/ssh"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 主机
|
// 主机
|
||||||
type Host struct {
|
type Host struct {
|
||||||
Id int16 `xorm:"smallint pk autoincr"`
|
Id int16 `xorm:"smallint pk autoincr"`
|
||||||
|
@ -9,6 +13,8 @@ type Host struct {
|
||||||
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
|
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
|
||||||
Port int `xorm:"notnull default 22"` // 主机端口
|
Port int `xorm:"notnull default 22"` // 主机端口
|
||||||
Remark string `xorm:"varchar(512) notnull default '' "` // 备注
|
Remark string `xorm:"varchar(512) notnull default '' "` // 备注
|
||||||
|
AuthType ssh.HostAuthType `xorm:"tinyint notnull default 1"` // 认证方式 1: 密码 2: 公钥
|
||||||
|
PrivateKey string `xorm:"varchar(4096) notnull default '' "` // 私钥
|
||||||
Page int `xorm:"-"`
|
Page int `xorm:"-"`
|
||||||
PageSize int `xorm:"-"`
|
PageSize int `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
@ -23,6 +29,11 @@ func (host *Host) Create() (insertId int16, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (host *Host) UpdateBean() (int64, error) {
|
||||||
|
return Db.Cols("name,alias,username,password,port,remark,auth_type,private_key").Update(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
func (host *Host) Update(id int, data CommonMap) (int64, error) {
|
func (host *Host) Update(id int, data CommonMap) (int64, error) {
|
||||||
return Db.Table(host).ID(id).Update(data)
|
return Db.Table(host).ID(id).Update(data)
|
||||||
|
@ -39,9 +50,13 @@ func (host *Host) Find(id int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) NameExists(name string) (bool, error) {
|
func (host *Host) NameExists(name string, id int16) (bool, error) {
|
||||||
count, err := Db.Where("name = ?", name).Count(host);
|
if id == 0 {
|
||||||
|
count, err := Db.Where("name = ?", name).Count(host);
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host);
|
||||||
return count > 0, err
|
return count > 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
"github.com/ouqiang/gocron/modules/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TaskProtocol int8
|
type TaskProtocol int8
|
||||||
|
@ -36,6 +37,8 @@ type TaskHost struct {
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
Alias string
|
Alias string
|
||||||
|
AuthType ssh.HostAuthType
|
||||||
|
PrivateKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (TaskHost) TableName() string {
|
func (TaskHost) TableName() string {
|
||||||
|
@ -52,7 +55,7 @@ func (task *Task) Create() (insertId int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
func (task *Task) UpdateBean() (int64, error) {
|
||||||
return Db.UseBool("status").Update(task)
|
return Db.UseBool("status").Update(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,15 +79,26 @@ func (task *Task) Enable(id int) (int64, error) {
|
||||||
return task.Update(id, CommonMap{"status": Enabled})
|
return task.Update(id, CommonMap{"status": Enabled})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取所有激活任务
|
||||||
func (task *Task) ActiveList() ([]TaskHost, error) {
|
func (task *Task) ActiveList() ([]TaskHost, error) {
|
||||||
task.parsePageAndPageSize()
|
task.parsePageAndPageSize()
|
||||||
list := make([]TaskHost, 0)
|
list := make([]TaskHost, 0)
|
||||||
fields := "t.*, host.name,host.username,host.password,host.port"
|
fields := "t.*, host.alias,host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||||
err := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id").Where("t.status = ?", Enabled).Cols(fields).Find(&list)
|
err := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id").Where("t.status = ?", Enabled).Cols(fields).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取某个主机下的所有激活任务
|
||||||
|
func (task *Task) ActiveListByHostId(hostId int16) ([]TaskHost, error) {
|
||||||
|
task.parsePageAndPageSize()
|
||||||
|
list := make([]TaskHost, 0)
|
||||||
|
fields := "t.*, host.alias,host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||||
|
err := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id").Where("t.status = ? AND t.host_id = ?", Enabled, hostId).Cols(fields).Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
// 判断主机id是否有引用
|
// 判断主机id是否有引用
|
||||||
func (task *Task) HostIdExist(hostId int16) (bool, error) {
|
func (task *Task) HostIdExist(hostId int16) (bool, error) {
|
||||||
count, err := Db.Where("host_id = ?", hostId).Count(task);
|
count, err := Db.Where("host_id = ?", hostId).Count(task);
|
||||||
|
@ -105,7 +119,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.name,host.username,host.password,host.port"
|
fields := "t.*, host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||||
_, err := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id").Where("t.id=?", id).Cols(fields).Get(&taskHost)
|
_, err := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id").Where("t.id=?", id).Cols(fields).Get(&taskHost)
|
||||||
|
|
||||||
return taskHost, err
|
return taskHost, err
|
||||||
|
|
|
@ -8,9 +8,22 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type HostAuthType int8 // 认证方式
|
||||||
|
|
||||||
|
const (
|
||||||
|
HostPassword = 1 // 密码认证
|
||||||
|
HostPublicKey = 2 // 公钥认证
|
||||||
|
)
|
||||||
|
|
||||||
|
const SSHConnectTimeout = 10
|
||||||
|
|
||||||
|
|
||||||
type SSHConfig struct {
|
type SSHConfig struct {
|
||||||
|
AuthType HostAuthType
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
|
PrivateKey string
|
||||||
Host string
|
Host string
|
||||||
Port int
|
Port int
|
||||||
ExecTimeout int// 执行超时时间
|
ExecTimeout int// 执行超时时间
|
||||||
|
@ -21,6 +34,45 @@ type Result struct {
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) {
|
||||||
|
timeout := SSHConnectTimeout * time.Second
|
||||||
|
// 密码认证
|
||||||
|
if sshConfig.AuthType == HostPassword {
|
||||||
|
config = &ssh.ClientConfig{
|
||||||
|
User: sshConfig.User,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(sshConfig.Password),
|
||||||
|
},
|
||||||
|
Timeout: timeout,
|
||||||
|
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公钥认证
|
||||||
|
config = &ssh.ClientConfig{
|
||||||
|
User: sshConfig.User,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.PublicKeys(signer),
|
||||||
|
},
|
||||||
|
Timeout: timeout,
|
||||||
|
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 执行shell命令
|
// 执行shell命令
|
||||||
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
||||||
client, err := getClient(sshConfig)
|
client, err := getClient(sshConfig)
|
||||||
|
@ -56,15 +108,9 @@ func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
||||||
config := &ssh.ClientConfig{
|
config, err := parseSSHConfig(sshConfig)
|
||||||
User: sshConfig.User,
|
if err != nil {
|
||||||
Auth: []ssh.AuthMethod{
|
return nil, err
|
||||||
ssh.Password(sshConfig.Password),
|
|
||||||
},
|
|
||||||
Timeout: 10 * time.Second,
|
|
||||||
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
||||||
|
|
||||||
|
@ -80,3 +126,4 @@ func triggerTimeout(ch chan bool, timeout int){
|
||||||
time.Sleep(time.Duration(timeout) * time.Second)
|
time.Sleep(time.Duration(timeout) * time.Second)
|
||||||
close(ch)
|
close(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,36 +4,43 @@
|
||||||
|
|
||||||
function Util() {
|
function Util() {
|
||||||
var util = {};
|
var util = {};
|
||||||
util.post = function(url, params, callback) {
|
var SUCCESS = 0; // 操作成功
|
||||||
// 用户认证失败
|
var FAILURE_MESSAGE = '操作失败';
|
||||||
var SUCCESS = 0;
|
util.ajaxSuccess = function(response, callback) {
|
||||||
var FAILURE = 1;
|
if (response.code === undefined) {
|
||||||
var NOT_FOUND = 2;
|
swal(FAILURE_MESSAGE, '服务端返回值无法解析', 'error');
|
||||||
var AUTH_ERROR = 3;
|
return;
|
||||||
|
}
|
||||||
|
if (response.code != SUCCESS) {
|
||||||
|
swal(FAILURE_MESSAGE, response.message ,'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(response.code, response.message, response.data);
|
||||||
|
};
|
||||||
|
util.ajaxFailure = function() {
|
||||||
|
// todo 错误处理
|
||||||
|
swal(FAILURE_MESSAGE, '未知错误', 'error');
|
||||||
|
};
|
||||||
|
util.get = function(url, callback) {
|
||||||
|
var SUCCESS = 0; // 操作成功
|
||||||
var FAILURE_MESSAGE = '操作失败';
|
var FAILURE_MESSAGE = '操作失败';
|
||||||
|
$.get(
|
||||||
|
url,
|
||||||
|
function(response) {
|
||||||
|
util.ajaxSuccess(response, callback);
|
||||||
|
},
|
||||||
|
'json'
|
||||||
|
).error(util.ajaxFailure);
|
||||||
|
};
|
||||||
|
util.post = function(url, params, callback) {
|
||||||
$.post(
|
$.post(
|
||||||
url,
|
url,
|
||||||
params,
|
params,
|
||||||
function(response) {
|
function(response) {
|
||||||
if (response.code === undefined) {
|
util.ajaxSuccess(response, callback);
|
||||||
swal(FAILURE_MESSAGE, '服务端返回值无法解析', 'error');
|
|
||||||
}
|
|
||||||
if (response.code == AUTH_ERROR) {
|
|
||||||
swal(FAILURE_MESSAGE, response.message, 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (response.code == NOT_FOUND) {
|
|
||||||
swal(FAILURE_MESSAGE, response.message, 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (response.code == FAILURE) {
|
|
||||||
swal(FAILURE_MESSAGE, response.message ,'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback(response.code, response.message, response.data);
|
|
||||||
},
|
},
|
||||||
'json'
|
'json'
|
||||||
)
|
).error(util.ajaxFailure);
|
||||||
};
|
};
|
||||||
util.confirm = function(message, callback) {
|
util.confirm = function(message, callback) {
|
||||||
swal({
|
swal({
|
||||||
|
|
|
@ -6,6 +6,8 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Index(ctx *macaron.Context) {
|
func Index(ctx *macaron.Context) {
|
||||||
|
@ -36,36 +38,87 @@ 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{
|
||||||
|
User: hostModel.Username,
|
||||||
|
Password: hostModel.Password,
|
||||||
|
Host: hostModel.Name,
|
||||||
|
Port: hostModel.Port,
|
||||||
|
ExecTimeout: 5,
|
||||||
|
AuthType: hostModel.AuthType,
|
||||||
|
PrivateKey: hostModel.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
|
||||||
Name string `binding:"Required;MaxSize(100)"`
|
Name string `binding:"Required;MaxSize(100)"`
|
||||||
Alias string `binding:"Required;MaxSize(32)"`
|
Alias string `binding:"Required;MaxSize(32)"`
|
||||||
Username string `binding:"Required;MaxSize(32)"`
|
Username string `binding:"Required;MaxSize(32)"`
|
||||||
Password string `binding:"Required;MaxSize(64)"`
|
Password string
|
||||||
Port int `binding:"Required;Range(1-65535)"`
|
Port int `binding:"Required;Range(1-65535)"`
|
||||||
|
AuthType ssh.HostAuthType `binding:"Required:Range(1,2)"`
|
||||||
|
PrivateKey string
|
||||||
Remark string
|
Remark string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Store(ctx *macaron.Context, form HostForm) string {
|
func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
json := utils.JsonResponse{}
|
json := utils.JsonResponse{}
|
||||||
hostModel := new(models.Host)
|
hostModel := new(models.Host)
|
||||||
nameExist, err := hostModel.NameExists(form.Name)
|
id := form.Id
|
||||||
|
nameExist, err := hostModel.NameExists(form.Name, form.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return json.CommonFailure("操作失败", err)
|
return json.CommonFailure("操作失败", err)
|
||||||
}
|
}
|
||||||
if nameExist {
|
if nameExist {
|
||||||
return json.CommonFailure("主机名已存在")
|
return json.CommonFailure("主机名已存在")
|
||||||
}
|
}
|
||||||
|
if form.Id > 0 {
|
||||||
|
hostModel.Id = form.Id
|
||||||
|
}
|
||||||
hostModel.Name = form.Name
|
hostModel.Name = form.Name
|
||||||
hostModel.Alias = form.Alias
|
hostModel.Alias = form.Alias
|
||||||
hostModel.Username = form.Username
|
hostModel.Username = form.Username
|
||||||
hostModel.Password = form.Password
|
hostModel.Password = form.Password
|
||||||
hostModel.Port = form.Port
|
hostModel.Port = form.Port
|
||||||
hostModel.Remark = form.Remark
|
hostModel.Remark = form.Remark
|
||||||
_, err = hostModel.Create()
|
hostModel.PrivateKey = form.PrivateKey
|
||||||
|
hostModel.AuthType = form.AuthType
|
||||||
|
isCreate := false
|
||||||
|
if id > 0 {
|
||||||
|
_, err = hostModel.UpdateBean()
|
||||||
|
} else {
|
||||||
|
isCreate = true
|
||||||
|
id, err = hostModel.Create()
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return json.CommonFailure("保存失败", err)
|
return json.CommonFailure("保存失败", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
taskModel := new(models.TaskHost)
|
||||||
|
tasks, err := taskModel.ActiveListByHostId(id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("刷新任务主机信息失败", err)
|
||||||
|
}
|
||||||
|
if !isCreate && len(tasks) > 0 {
|
||||||
|
serviceTask := new(service.Task)
|
||||||
|
serviceTask.BatchAdd(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
return json.Success("保存成功", nil)
|
return json.Success("保存成功", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,8 @@ 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", 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)
|
||||||
|
|
|
@ -45,7 +45,7 @@ func Edit(ctx *macaron.Context) {
|
||||||
}
|
}
|
||||||
taskModel := new(models.Task)
|
taskModel := new(models.Task)
|
||||||
task, err := taskModel.Detail(id)
|
task, err := taskModel.Detail(id)
|
||||||
if err != nil || taskModel.Id != id {
|
if err != nil || task.Id != id {
|
||||||
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error())
|
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error())
|
||||||
ctx.Redirect("/task")
|
ctx.Redirect("/task")
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,9 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
return json.CommonFailure("请选择主机名")
|
return json.CommonFailure("请选择主机名")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.Id > 0 {
|
||||||
|
taskModel.Id = form.Id
|
||||||
|
}
|
||||||
taskModel.Name = form.Name
|
taskModel.Name = form.Name
|
||||||
taskModel.Protocol = form.Protocol
|
taskModel.Protocol = form.Protocol
|
||||||
taskModel.Command = form.Command
|
taskModel.Command = form.Command
|
||||||
|
@ -106,7 +109,7 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
id, err = taskModel.Create()
|
id, err = taskModel.Create()
|
||||||
} else {
|
} else {
|
||||||
_, err = taskModel.UpdateBean(id)
|
_, err = taskModel.UpdateBean()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return json.CommonFailure("保存失败", err)
|
return json.CommonFailure("保存失败", err)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
"github.com/ouqiang/gocron/modules/logger"
|
||||||
"github.com/ouqiang/gocron/modules/ssh"
|
"github.com/ouqiang/gocron/modules/ssh"
|
||||||
"github.com/jakecoffman/cron"
|
"github.com/jakecoffman/cron"
|
||||||
"strings"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
"github.com/ouqiang/gocron/modules/utils"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
@ -32,7 +31,12 @@ func (task *Task) Initialize() {
|
||||||
logger.Debug("任务列表为空")
|
logger.Debug("任务列表为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, item := range taskList {
|
task.BatchAdd(taskList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量添加任务
|
||||||
|
func (task *Task) BatchAdd(tasks []models.TaskHost) {
|
||||||
|
for _, item := range tasks {
|
||||||
task.Add(item)
|
task.Add(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,14 +69,10 @@ func (h *LocalCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
||||||
if taskModel.Command == "" {
|
if taskModel.Command == "" {
|
||||||
return "", errors.New("invalid command")
|
return "", errors.New("invalid command")
|
||||||
}
|
}
|
||||||
fields := strings.Split(taskModel.Command, " ")
|
|
||||||
var args []string
|
args := []string{"-c", taskModel.Command}
|
||||||
if len(fields) > 1 {
|
|
||||||
args = fields[1:]
|
return utils.ExecShellWithTimeout(taskModel.Timeout, "/bin/bash", args...)
|
||||||
} else {
|
|
||||||
args = []string{}
|
|
||||||
}
|
|
||||||
return utils.ExecShellWithTimeout(taskModel.Timeout, fields[0], args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP任务
|
// HTTP任务
|
||||||
|
@ -83,7 +83,7 @@ func (h *HTTPHandler) Run(taskModel models.TaskHost) (result string, err error)
|
||||||
if taskModel.Timeout > 0 {
|
if taskModel.Timeout > 0 {
|
||||||
client.Timeout = time.Duration(taskModel.Timeout) * time.Second
|
client.Timeout = time.Duration(taskModel.Timeout) * time.Second
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", taskModel.Command, nil)
|
req, err := http.NewRequest("GET", taskModel.Command, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("任务处理#创建HTTP请求错误-", err.Error())
|
logger.Error("任务处理#创建HTTP请求错误-", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -119,6 +119,8 @@ func (h *SSHCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
||||||
Host: taskModel.Name,
|
Host: taskModel.Name,
|
||||||
Port: taskModel.Port,
|
Port: taskModel.Port,
|
||||||
ExecTimeout: taskModel.Timeout,
|
ExecTimeout: taskModel.Timeout,
|
||||||
|
AuthType: taskModel.AuthType,
|
||||||
|
PrivateKey: taskModel.PrivateKey,
|
||||||
}
|
}
|
||||||
return ssh.Exec(sshConfig, taskModel.Command)
|
return ssh.Exec(sshConfig, taskModel.Command)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +134,7 @@ func createTaskLog(taskModel models.TaskHost) (int64, error) {
|
||||||
taskLogModel.Protocol = taskModel.Protocol
|
taskLogModel.Protocol = taskModel.Protocol
|
||||||
taskLogModel.Command = taskModel.Command
|
taskLogModel.Command = taskModel.Command
|
||||||
taskLogModel.Timeout = taskModel.Timeout
|
taskLogModel.Timeout = taskModel.Timeout
|
||||||
taskLogModel.Hostname = taskModel.Name
|
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
|
||||||
taskLogModel.StartTime = time.Now()
|
taskLogModel.StartTime = time.Now()
|
||||||
taskLogModel.Status = models.Running
|
taskLogModel.Status = models.Running
|
||||||
insertId, err := taskLogModel.Create()
|
insertId, err := taskLogModel.Create()
|
||||||
|
|
|
@ -10,46 +10,72 @@
|
||||||
<h3 class="ui dividing header">
|
<h3 class="ui dividing header">
|
||||||
<i class="large add icon"></i>
|
<i class="large add icon"></i>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
添加主机
|
{{{.Title}}}
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="ui form fluid vertical segment">
|
<form class="ui form fluid vertical segment">
|
||||||
<div class="two fields">
|
<input type="hidden" name="id" value="{{{.Host.Id}}}">
|
||||||
|
<div class="four fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>主机名 (域名或IP)</label>
|
<label>主机名 (域名或IP)</label>
|
||||||
<div class="ui small left icon input">
|
<div class="ui small left icon input">
|
||||||
<input type="text" placeholder="127.0.0.1" name="name">
|
<input type="text" name="name" value="{{{.Host.Name}}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>主机别名 (方便记忆和引用)</label>
|
<label>主机别名 (方便记忆和引用)</label>
|
||||||
<div class="ui small left icon input">
|
<div class="ui small left icon input">
|
||||||
<input type="text" placeholder="db" name="alias">
|
<input type="text" name="alias" value="{{{.Host.Alias}}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="two fields">
|
<div class="four fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>SSH用户名</label>
|
<label>SSH用户名</label>
|
||||||
<div class="ui small left icon input">
|
<div class="ui small left icon input">
|
||||||
<input type="text" placeholder="root" name="username">
|
<input type="text" name="username" value="{{{.Host.Username}}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label>SSH密码</label>
|
|
||||||
<div class="ui small left icon input">
|
|
||||||
<input type="text" placeholder="123456" name="password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="two fields">
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>SSH端口</label>
|
<label>SSH端口</label>
|
||||||
<div class="ui small left icon input">
|
<div class="ui small left icon input">
|
||||||
<input type="text" placeholder="22" name="port" value="22">
|
<input type="text" name="port" value="{{{.Host.Port}}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="four fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>认证方式</label>
|
||||||
|
<div class="ui dropdown selection">
|
||||||
|
{{{ if .Host }}}
|
||||||
|
<input type="hidden" name="auth_type" value="{{{if eq .Host.AuthType 1 }}}1{{{else}}}2{{{end}}}">
|
||||||
|
{{{else}}}
|
||||||
|
<input type="hidden" name="auth_type" value="2">
|
||||||
|
{{{end}}}
|
||||||
|
<div class="default text">公钥</div>
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="2">公钥</div>
|
||||||
|
<div class="item" data-value="1">密码</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="two fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>私钥 (~/.ssh/id_rsa)</label>
|
||||||
|
<div class="ui small left icon input">
|
||||||
|
<textarea rows="7" name="private_key">{{{.Host.PrivateKey}}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="four fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>SSH密码</label>
|
||||||
|
<div class="ui small left icon input">
|
||||||
|
<input type="text" placeholder="" name="password" value="{{{.Host.Password}}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +83,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>备注</label>
|
<label>备注</label>
|
||||||
<div class="ui small left icon input">
|
<div class="ui small left icon input">
|
||||||
<textarea rows="5" name="remark" placeholder="数据库服务器"></textarea>
|
<textarea rows="7" name="remark" >{{{.Host.Remark}}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
<th>主机名</th>
|
<th>主机名</th>
|
||||||
<th>别名</th>
|
<th>别名</th>
|
||||||
<th>用户名</th>
|
<th>用户名</th>
|
||||||
<th>密码</th>
|
|
||||||
<th>端口</th>
|
<th>端口</th>
|
||||||
|
<th>任务数量</th>
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -32,14 +32,13 @@
|
||||||
<td>{{{.Name}}}</td>
|
<td>{{{.Name}}}</td>
|
||||||
<td>{{{.Alias}}}</td>
|
<td>{{{.Alias}}}</td>
|
||||||
<td>{{{.Username}}}</td>
|
<td>{{{.Username}}}</td>
|
||||||
<td>{{{.Password}}}</td>
|
|
||||||
<td>{{{.Port}}}</td>
|
<td>{{{.Port}}}</td>
|
||||||
|
<td></td>
|
||||||
<td>{{{.Remark}}}</td>
|
<td>{{{.Remark}}}</td>
|
||||||
<td>
|
<td id="operation">
|
||||||
<button class="ui pink button" >编辑</button>
|
<a class="ui purple button" href="/host/edit/{{{.Id}}}">编辑</a>
|
||||||
<button class="ui positive button" onclick="util.removeConfirm('/host/remove/{{{.Id}}}')">删除</button>
|
<button class="ui positive button" onclick="util.removeConfirm('/host/remove/{{{.Id}}}')">删除</button>
|
||||||
<button class="ui pink button" >连接测试</button>
|
<button class="ui blue button" @click="ping({{{.Id}}})">连接测试</button>
|
||||||
<button class="ui pink button" >查看任务</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
|
@ -48,4 +47,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var Vue = new Vue({
|
||||||
|
el: '#operation',
|
||||||
|
methods: {
|
||||||
|
ping: function(id) {
|
||||||
|
swal({
|
||||||
|
title: '',
|
||||||
|
text: "连接中.......",
|
||||||
|
type: 'info',
|
||||||
|
closeOnConfirm: true
|
||||||
|
});
|
||||||
|
util.get("/host/ping/" + id, function(code, message) {
|
||||||
|
swal('操作成功', '连接成功', 'success');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{{{ template "common/footer" . }}}
|
{{{ template "common/footer" . }}}
|
|
@ -46,7 +46,7 @@
|
||||||
<button class="ui blue button" onclick="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
|
<button class="ui blue button" onclick="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
<button class="ui positive button" onclick="util.removeConfirm('/task/remove/{{{.Id}}}')">删除</button>
|
<button class="ui positive button" onclick="util.removeConfirm('/task/remove/{{{.Id}}}')">删除</button>
|
||||||
<button class="ui pink button">查看日志</button>
|
<button class="ui instagram button">查看日志</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
{{{if eq $.Task.HostId .Id}}} checked {{{end}}}
|
{{{if eq $.Task.HostId .Id}}} checked {{{end}}}
|
||||||
>
|
>
|
||||||
{{{else}}}
|
{{{else}}}
|
||||||
<input type="radio" name="host_id" tabindex="0" class="hidden">
|
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}">
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
<label>{{{.Alias}}}-{{{.Name}}}</label>
|
<label>{{{.Alias}}}-{{{.Name}}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue