目录

GO第三方库-go-ansible

调用 ansible 执行自动化任务.

使用介绍

作为一名运维,日常操作经常会用到 ansible 来批量执行任务。如果我们想在编程语言里面调用它,对 Python 来说相对比较简单,但是对于 golang 就比较麻烦了。一种方法是自己封装,一种是使用第三方库。

自己封装

自己封装的原理是通过 exec 标准库,调用系统上的 ansible 来执行任务。

简单封装示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package ansible

import (
    "bytes"
    "os/exec"
)

type Ansible struct {
    inventoryFile string
    user          string
    privateKey    string
}

// 构造函数,构造 ansible 运行所需参数
func NewAnsible(inventoryFile, user, privateKey string) *Ansible {
    return &Ansible{
        inventoryFile: inventoryFile,
        user:          user,
        privateKey:    privateKey,
    }
}

// 运行接口
func (a *Ansible) RunPlaybook(playbookPath string) (string, error) {
    var stdout bytes.Buffer
    var stderr bytes.Buffer

    cmd := exec.Command("ansible-playbook",
        "-i", a.inventoryFile,
        "-u", a.user,
        "--private-key="+a.privateKey,
        playbookPath)

    cmd.Stdout = &stdout
    cmd.Stderr = &stderr

    err := cmd.Run()

	// 返回结果
    return stdout.String() + stderr.String(), err
}

但是自己封装耗时耗力,还可能有潜在的 bug 。

第三方库

在 github 上面,我找到了go-ansible这个库,别人已经封装好了的。用起来还挺顺手的。他的原理也是通过 exec 标准库,调用系统上的 ansible 来执行任务的。

我们拿其中一个场景出来看看,详情注释部分写得很清楚了,不再赘述。如果有兴趣可以通上面的超链接直接到官方 github 查看其他各种示例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package main

import (
	"context"
	"flag"
	"fmt"
	"time"
	"bytes"
	"io"

	"github.com/apenella/go-ansible/pkg/execute"
	"github.com/apenella/go-ansible/pkg/execute/measure"
	"github.com/apenella/go-ansible/pkg/options"
	"github.com/apenella/go-ansible/pkg/playbook"
	"github.com/apenella/go-ansible/pkg/stdoutcallback/results"
)

func main() {

	var res *results.AnsiblePlaybookJSONResults
	var timeout int
	flag.IntVar(&timeout, "timeout", 32400, "Timeout in seconds")
	flag.Parse()

	buff := new(bytes.Buffer)

	fmt.Printf("Timeout: %d seconds\n", timeout)

	// 配置超时时间
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()

	// 配置连接方式, "local" 表示,无论你后面 Inventory 选项怎么配,都是在执行 ansible 的本地执行
	// 要真正连接到 Inventory 配置的机器,注释掉 Connection 选项或者使用 "smart" 或 "ssh" 作为参数值
	ansiblePlaybookConnectionOptions := &options.AnsibleConnectionOptions{
		Connection: "local",
		// User:       "apenella",
	}

	// 资产清单和变量文件,也可以是一个 map 类型来作为变量,就无需引入文件
	ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
		Inventory: "192.168.21.101,",
		ExtraVarsFile: []string{
			"@vars-file1.yml",
		},
	}

	// 执行结果缓存
	executorTimeMeasurement := measure.NewExecutorTimeMeasurement(
		execute.NewDefaultExecute(
			execute.WithWrite(io.Writer(buff)),
		),
	)

	// 构造 ansible 
	playbook := &playbook.AnsiblePlaybookCmd{
		Playbooks:         []string{"site.yml"},
		ConnectionOptions: ansiblePlaybookConnectionOptions,
		Options:           ansiblePlaybookOptions,
		Exec: execute.NewDefaultExecute(
			execute.WithWrite(io.Writer(buff)),
		),
		// Exec: executorTimeMeasurement,
		StdoutCallback: "json",
	}

	// 执行 playbook
	err := playbook.Run(ctx)
	if err != nil {
		panic(err)
	}
	
	// 输出结果
	res, err = results.ParseJSONResultsStream(io.Reader(buff))
	if err != nil {
		panic(err)
	}
}

总结

使用别人封装好的第三方库,就不需要自己耗时耗力去重复造轮子,可以把更多的精力用在业务上。