Go 版批量执行命令工具

ansible 真的太慢了,而且依赖对端python环境,对于交换机,分流等基于ssh连接的网络设备就没办法了。用的最多的是批量执行命令和下发文件,
我们就借鉴ansible的执行方式,先准备yaml格式hosts ,文件名我是写死了,所以要先创建文件roomname.yaml
例子:

groups:
  - roomname: idgoup1
    ip:
        - 192.168.1.201 root 123456
        - 192.168.1.30 root 123456
  - roomname: idgroup2
    ip:
        - 192.2.1.134 admin admin123
        - 192.2.1.135 admin admin123

直接上代码。

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/sftp"

	"golang.org/x/crypto/ssh"
	"gopkg.in/yaml.v2"
)

var ipAddrs chan string = make(chan string)
var wg sync.WaitGroup

type HostInfo struct {
	Roomname string   `yaml:"roomname"`
	IpList   []string `yaml:"ip"`
}

type Group struct {
	List []*HostInfo `yaml:"groups"`
}

func OpencfgFile() {

	databytes, err := ioutil.ReadFile("roomname.yaml")

	if err != nil {
		log.Fatal(err)
	}
	g := new(Group)
	if err = yaml.Unmarshal(databytes, &g); err != nil {
		log.Fatal(err)
	}

	for _, hostinfo := range g.List {
		if os.Args[1] == "all" {

			for _, i := range hostinfo.IpList {
				ipAddrs <- hostinfo.Roomname + " " + i

			}

		} else if hostinfo.Roomname == os.Args[1] {

			for _, i := range hostinfo.IpList {

				ipAddrs <- hostinfo.Roomname + " " + i
			}

		}

	}
	close(ipAddrs)
}

func Opsee() {
	for s := range ipAddrs {
		wg.Add(1)
		str1 := strings.Split(s, " ")
		room := &str1[0]
		ip := &str1[1]
		user := &str1[2]
		passwd := &str1[3]
		comm := os.Args[2]
		if os.Args[2] == "scp" {
			go SftpRun(*room, *user, *passwd, *ip)

		} else {
			go Runshell(*room, *user, *passwd, *ip, comm)
		}

	}
	wg.Wait()
	if len(ipAddrs) == 0 {
		fmt.Println("End of channel data reading")
	}
}

func sshSession(user, password, host string, port int) (sshSession *ssh.Session, err error) {
	sshClient, err := connector(user, password, host, port)
	if err != nil {
		//fmt.Println(room + "." + ip)
		//fmt.Println("连接失败", err)
		return
	}

	if sshSession, err = sshClient.NewSession(); err != nil {
		fmt.Println("创建客户端失败", err)
		return
	}

	return
}

func connector(user, password, host string, port int) (sshClient *ssh.Client, err error) {
	auth := make([]ssh.AuthMethod, 0)
	auth = append(auth, ssh.Password(password))

	clientConfig := &ssh.ClientConfig{
		User:    user,
		Auth:    auth,
		Timeout: 1 * time.Second,
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	addr := host + ":" + strconv.Itoa(port)
	sshClient, err = ssh.Dial("tcp", addr, clientConfig)
	if err != nil {
		//fmt.Println("连接ssh失败", err)
		return
	}

	return
}
func sftpconnect(user, password, host string, port int) (*sftp.Client, error) {
	var (
		auth         []ssh.AuthMethod
		addr         string
		clientConfig *ssh.ClientConfig
		sshClient    *ssh.Client
		sftpClient   *sftp.Client
		err          error
	)

	auth = make([]ssh.AuthMethod, 0)
	auth = append(auth, ssh.Password(password))

	clientConfig = &ssh.ClientConfig{
		User:    user,
		Auth:    auth,
		Timeout: 30 * time.Second,
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}

	addr = fmt.Sprintf("%s:%d", host, port)

	if sshClient, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
		return nil, err
	}

	if sftpClient, err = sftp.NewClient(sshClient); err != nil {
		return nil, err
	}

	return sftpClient, nil
}
func SftpRun(room, user, passwd, ip string) {
	defer wg.Done()
	var (
		sftpClient *sftp.Client
	)

	sftpClient, _ = sftpconnect(user, passwd, ip, 22)

	defer sftpClient.Close()

	localFilePath := os.Args[3]
	remoteDir := os.Args[4]
	srcFile, err := os.Open(localFilePath)
	if err != nil {
		log.Fatal(err)
	}
	defer srcFile.Close()

	var remoteFileName = path.Base(localFilePath)
	dstFile, err := sftpClient.Create(path.Join(remoteDir, remoteFileName))
	if err != nil {
		log.Fatal(err)
	}
	defer dstFile.Close()

	buf := make([]byte, 1024)
	for {
		n, _ := srcFile.Read(buf)
		if n == 0 {
			break
		}
		dstFile.Write(buf[0:n])
	}

	fmt.Println(room + "." + ip + "\n" + "copy file  finished!")
}
func Runshell(room, user, passwd, ip, comm string) {

	defer wg.Done()
	session, err := sshSession(user, passwd, ip, 22)

	if err != nil {

		log.Printf(room+"."+ip+"\n"+"result:", err)
		return
	}

	buf, _ := session.CombinedOutput(comm)

	fmt.Println(room + "." + ip + "\n" + "result:" + string(buf))
	defer session.Close()

}
func main() {
	flag.Parse()
	if flag.NArg() == 2 && os.Args[2] != "scp" {
		go OpencfgFile()
		Opsee()

	} else if flag.NArg() == 4 && os.Args[2] == "scp" {
		go OpencfgFile()
		Opsee()
	} else {

		fmt.Println("本go 只有两个功能,批量执行命令和scp下发文件" + "\n" + "正确执行方式,1:hosts组,2:执行的命令;" + "\n" + "例如 ./gosh all \"date\";" + "\n" + "如果下发文件,1:hosts组,2:scp 3:本地文件路径,4:远端目录;" + "\n" + "例如 ./gosh all scp \"/tmp/test.log\" \"/tmp\"")
		return
	}

}

执行方式

在这里插入图片描述

效果:

在这里插入图片描述
如果机器连通性没问题的话,基本上是秒出结果的了。
很多类似的程序,都是在重复造轮子。
轮子千千万万,适合自己的轮子才是好的轮子。
这里有编译好的包
https://gitee.com/djyou32/gosh

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值