一、自定义创建模块
1
2
3
4
5
6
7
8
|
[root@localhost:]
# tree -L 3 salt
salt
├── etc
├── _grains
│ ├── dmp_scribe.py
│ └── zabbix_conf.py
├── _modules
│ └── ip.py
|
通过上图可以看到已经创建了一个名为ip.py文件,看看相关内容:
1
2
3
4
5
6
7
8
9
10
|
#!/usr/bin/env python
import
commands
def
eth1():
ip
=
{}
cmd
=
"ifconfig eth1|grep inet|awk '{print $2}'|awk -F : '{print $2}'"
ip2
=
commands.getoutput(cmd).strip()
ip[
'ip'
]
=
ip2
return
ip
|
定义模块我们没有在/srv/salt/top.sls文件中新增模块名,现在把写好的模块同步到你需要执行的机器上,可以全部同步,也可以针对性的同步
这里只演示同步一台
1
2
3
|
salt
'192.168.10.128'
saltutil.sync_modules
192.168
.
10.128
:
-
modules.ip
|
在master执行编写的模块,由于不依赖state.sls文件,所以执行时命令也有变化,在主要命令后面跟模块名●函数名
1
2
3
4
5
|
[root@localhost _modules]
# salt '192.168.10.128' ip.eth1
192.168
.
10.128
:
-
-
-
-
-
-
-
-
-
-
ip:
192.168
.
10.128
|
二、屏幕返回值输出到指定的位置
saltstack执行命令时默认的返回值都是打印在当前屏幕上的,如果一次执行多台机器,怎么查看这些记录,为了更方便的查看这些返回结果的信息,这就用到了salt的returners功能了。关于详细介绍请参考官网地址:http://docs.saltstack.cn/ref/returners/index.html?highlight=returners
在/srv/salt目录下创建_returners目录,将自己编写的模块都可以存放在该目录下。
1
2
3
4
5
|
├── _returners
│ ├── local_return.py
#输出到文件
│ ├── mysql_log.py
#输出到数据库
│ ├── nohup.out
│ ├── salt_event_to_mysql.py
|
在_returners目录下创建了两个文件,一个是local_return.py:表示把执行结果存在在本地
一个是mysql_log.py:表示把结果存放在mysql数据库中
1、返回结果存放到本地
1
2
3
4
5
6
7
8
|
#coding:utf-8
def
__virtual__():
'''调用时的名字'''
return
"local_return"
def
returner(ret):
f
=
open
(
'/var/log/salt/local_returner.log'
,
'a+'
)
f.write(
str
(ret)[
1
:
-
1
]
+
'\n'
)
f.close()
|
和自建模块一样,在编写完returners之后,也要同步到客户端的,同步的规则和自建模块一样。
1
|
salt
'192.168.10.128'
saltutil.sync_returners
|
在执行完上面的语句后,执行salt ‘192.168.10.128’cmd.run 'uptime' --return local_return
验证结果是否已经输出到文件中
2、返回结果存放在mysql中
官方默认returners中已经包含了mysql的returners,我们先直接利用官方原生的mysql returners,里面有创建相关表的语句。
官方的mysql returners源码在https://github.com/saltstack/salt/blob/develop/salt/returners/mysql.py
mysql服务器192.168.10.129:
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
|
#在mysql服务器上创建表
CREATE DATABASE `salt`
DEFAULT CHARACTER
SET
utf8
DEFAULT COLLATE utf8_general_ci;
USE `salt`;
-
-
-
-
Table structure
for
table `jids`
-
-
DROP TABLE IF EXISTS `jids`;
CREATE TABLE `jids` (
`jid` varchar(
255
) NOT NULL,
`load` mediumtext NOT NULL,
UNIQUE KEY `jid` (`jid`)
) ENGINE
=
InnoDB DEFAULT CHARSET
=
utf8;
-
-
-
-
Table structure
for
table `salt_returns`
-
-
DROP TABLE IF EXISTS `salt_returns`;
CREATE TABLE `salt_returns` (
`fun` varchar(
50
) NOT NULL,
`jid` varchar(
255
) NOT NULL,
`
return
` mediumtext NOT NULL,
`
id
` varchar(
255
) NOT NULL,
`success` varchar(
10
) NOT NULL,
`full_ret` mediumtext NOT NULL,
`alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY `
id
` (`
id
`),
KEY `jid` (`jid`),
KEY `fun` (`fun`)
) ENGINE
=
InnoDB DEFAULT CHARSET
=
utf8;
|
给相关salt表赋予访问权限:
1
|
GRANT
ALL
PRIVILEGES ON salt.
*
TO
'salt'
@localhost IDENTIFIED BY
'saltt'
;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
mysql> show databases;
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| Database |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| information_schema |
| salt |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
2
rows
in
set
(
0.00
sec)
mysql> use salt
mysql> show tables;
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| Tables_in_salt |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
| jids |
| salt_returners |
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
|
因为要把客户端执行命令的结果直接返回给mysql服务器,所以客户端也要配置mysql信息的,我们以192.168.10.128为例,在salt的配置文件中加入如下信息,你也可以把它单独写在一个文件中。为了方便管理推荐写在单独文件中:
1
2
3
4
5
6
|
#/etc/salt/minion.d/mysql.conf
mysql.host:
'192.168.1.204'
mysql.user:
'salt'
mysql.
pass
:
'salt'
mysql.db:
'salt'
mysql.port:
3306
|
然后在master上执行命令,如下:
1
2
|
salt
'192.168.10.128'
cmd.run
'uptime'
-
-
return
local_return
salt
'192,168.10.128'
cmd.run
'uptime'
-
-
return
mysql
|
注意这里用的是salt官网原生的mysql returners,并没有使用自己定义的mysql_log returners ,我们看看数据库中的记录:(图片是借鉴大神的图片)
第一句是刚创建好表时的结果,可以看到数据库中已经有一条记录了,下面自己定义一个mysql_log returners来试试:
1
|
salt
'192.168.10.128'
cmd.run
'hostname'
-
-
return
mysql_log
|
我们来数据库中查看一下记录:
下面附上定义的mysql_log.py:
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
79
80
81
|
#/usr/bin/python
#coding:utf-8
from
contextlib
import
contextmanager
import
sys
import
json
import
logging
# Import third party libs
try
:
import
MySQLdb
HAS_MYSQL
=
True
except
ImportError:
HAS_MYSQL
=
False
log
=
logging.getLogger(__name__)
def
__virtual__():
if
not
HAS_MYSQL:
return
False
return
"mysql_log"
def
_get_options():
'''
Returns options used for the MySQL connection.
'''
defaults
=
{
'host'
:
'192.168.1.204'
,
'user'
:
'salt'
,
'pass'
:
'salt'
,
'db'
:
'salt'
,
'port'
:
3306
}
_options
=
{}
# Ensure port is an int
for
attr
in
defaults:
_attr
=
__salt__[
'config.option'
](
'mysql.{0}'
.
format
(attr))
if
not
_attr:
log.debug(
'Using default for MySQL {0}'
.
format
(attr))
_options[attr]
=
defaults[attr]
continue
_options[attr]
=
_attr
return
_options
@contextmanager
def
_get_serv(commit
=
False
):
'''
Return a mysql cursor
'''
_options
=
_get_options()
conn
=
MySQLdb.connect(host
=
_options[
'host'
], user
=
_options[
'user'
], passwd
=
_options[
'pass'
], db
=
_options[
'db'
], port
=
_options[
'port'
])
cursor
=
conn.cursor()
try
:
yield
cursor
except
MySQLdb.DatabaseError as err:
error,
=
err.args
sys.stderr.write(
str
(error))
cursor.execute(
"ROLLBACK"
)
raise
err
else
:
if
commit:
cursor.execute(
"COMMIT"
)
else
:
cursor.execute(
"ROLLBACK"
)
finally
:
conn.close()
def
returner(ret):
'''
Return data to a mysql server
'''
with _get_serv(commit
=
True
) as cur:
sql
=
'''INSERT INTO `salt_returns`
(`fun`, `jid`, `return`, `id`, `success`, `full_ret` )
VALUES (%s, %s, %s, %s, %s, %s)'''
cur.execute(sql, (ret[
'fun'
], ret[
'jid'
],
str
(ret[
'return'
]), ret[
'id'
],
ret[
'success'
], json.dumps(ret)))
|
三、基于Salt Event系统构建Master端returner
上面我们介绍Saltstack的returner是由minion端主动连接returners完成执行结果的存储,在部分场景下并不能满足需求,由于salt底层已构建了一套Event系统,所有的操作均会产生event,因此基于Salt Event系统构建Master端returner成为一种可能。
SaltStack Event 系统 官网地址:http://docs.saltstack.com/en/latest/topics/event/index.html
SaltStack Event 系统监听events测试:http://pengyao.org/saltstack_event_system_listen_events.html
环境说明:
-
测试结构:Master/Minions结构
-
Salt Version:2015.8.8.2
-
本次测试结果将存放在mysql中
前置配置:
安装Mysqldb依赖: yum -y install MySQL-Python
配置本次测试需要使用的数据库及用户:
1
2
3
4
5
6
|
# 创建salt数据库
mysql
-
e
'create database salt'
# 创建用于连接salt数据库的用户
mysql
-
e
'"grant all on salt.* to salt@localhost identified by "salt_pass'
;
# 将数据库配置添加至master配置文件中
echo
-
e
"\n\n# MySQL\nmysql.host: 'localhost'\nmysql.user: 'salt'\nmysql.pass: 'salt_pass'\nmysql.db: 'salt'\nmysql.port: 3306"
>>
/
etc
/
salt
/
master
|
为了与salt自带的mysql returners兼容,本次直接使用mysql returners对应的数据库表结构:
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
|
USE `salt`;
-
-
-
-
Table structure
for
table `jids`
-
-
DROP TABLE IF EXISTS `jids`;
CREATE TABLE `jids` (
`jid` varchar(
255
) NOT NULL,
`load` mediumtext NOT NULL,
UNIQUE KEY `jid` (`jid`)
) ENGINE
=
InnoDB DEFAULT CHARSET
=
utf8;
-
-
-
-
Table structure
for
table `salt_returns`
-
-
DROP TABLE IF EXISTS `salt_returns`;
CREATE TABLE `salt_returns` (
`fun` varchar(
50
) NOT NULL,
`jid` varchar(
255
) NOT NULL,
`
return
` mediumtext NOT NULL,
`
id
` varchar(
255
) NOT NULL,
`success` varchar(
10
) NOT NULL,
`full_ret` mediumtext NOT NULL,
`alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY `
id
` (`
id
`),
KEY `jid` (`jid`),
KEY `fun` (`fun`)
) ENGINE
=
InnoDB DEFAULT CHARSET
=
utf8;
|
编写returners:salt_event_to_mysql.py
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
|
#!/bin/env python
#coding=utf8
# Import python libs
import
json
# Import salt modules
import
salt.config
import
salt.utils.event
# Import third party libs
import
MySQLdb
__opts__
=
salt.config.client_config(
'/etc/salt/master'
)
# Create MySQL connect
conn
=
MySQLdb.connect(host
=
__opts__[
'mysql.host'
], user
=
__opts__[
'mysql.user'
], passwd
=
__opts__[
'mysql.pass'
], db
=
__opts__[
'mysql.db'
], port
=
__opts__[
'mysql.port'
])
cursor
=
conn.cursor()
# Listen Salt Master Event System
event
=
salt.utils.event.MasterEvent(__opts__[
'sock_dir'
])
for
eachevent
in
event.iter_events(full
=
True
):
ret
=
eachevent[
'data'
]
if
"salt/job/"
in
eachevent[
'tag'
]:
# Return Event
if
ret.has_key(
'id'
)
and
ret.has_key(
'return'
):
# Igonre saltutil.find_job event
if
ret[
'fun'
]
=
=
"saltutil.find_job"
:
continue
sql
=
'''INSERT INTO `salt_returns`
(`fun`, `jid`, `return`, `id`, `success`, `full_ret` )
VALUES (%s, %s, %s, %s, %s, %s)'''
cursor.execute(sql, (ret[
'fun'
], ret[
'jid'
],
json.dumps(ret[
'return'
]), ret[
'id'
],
ret[
'success'
], json.dumps(ret)))
cursor.execute(
"COMMIT"
)
# Other Event
else
:
pass
|
运行编写的returner: Python salt_event_to_mysql.py
测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
salt
'*'
test.ping
#开启一个终端,运行salt指令
#输出为:
salt
-
minion
-
01.example
.com:
True
#检查mysql数据库,查询salt_returns表数据:
mysql salt
-
e
"select * from salt_returns\G"
#输出为:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
1.
row
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
fun: test.ping
jid:
20140417161103569310
return
: true
id
: salt
-
minion
-
01.example
.com
success:
1
full_ret: {
"fun_args"
: [],
"jid"
:
"20140417161103569310"
,
"return"
: true,
"retcode"
:
0
,
"success"
: true,
"cmd"
:
"_return"
,
"_stamp"
:
"2014-04-17T16:11:03.584859"
,
"fun"
:
"test.ping"
,
"id"
:
"salt-minion-01.example.com"
}
alter_time:
2014
-
04
-
17
16
:
11
:
03
#入库成功
|