install by vagrant
configuration
minion target pattern
salt modules
custom modules
salt state
salt http api
launch saltstack by vagrant
$ cat Vagrantfile
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
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant . configure ( 2 ) do | config |
config . vm . define :node1 do | node1 |
node1 . vm . box = "ubuntu1404"
node1 . vm . hostname = "salt-master"
node1 . vm . network :private_network , :ip => "172.16.200.100"
node1 . vm . network "public_network" , :bridge => "eth0" , auto_config: false
node1 . vm . synced_folder "." , "/vagrant" , disabled: true
node1 . vm . provision "shell" , inline: "apt-get install -y salt-master"
end
config . vm . define :node2 do | node2 |
node2 . vm . box = "ubuntu1404"
node2 . vm . hostname = "salt-minion"
node2 . vm . network :private_network , :ip => "172.16.200.101"
node2 . vm . network "public_network" , :bridge => "eth0" , auto_config: false
node2 . vm . synced_folder "." , "/vagrant" , disabled: true
node2 . vm . provision "shell" , inline: "apt-get install -y salt-minion"
end
config . vm . provider "virtualbox" do | vb |
vb . gui = false
vb . customize [ "modifyvm" , :id , "--memory" , "1024" ]
vb . customize [ "modifyvm" , :id , "--ioapic" , "on" ]
vb . customize [ "modifyvm" , :id , "--cpus" , "2" ]
#vb.customize ["modifyvm", :id, "--nic2", "intnet"]
#vb.customize ["modifyvm", :id, "--nictype2", "Am79C973"]
end
config . vm . provision "shell" do | shell |
shell . path = "./setup.sh"
end
end
$ cat setup.sh
1
2
3
4
5
6
#!/bin/bash
echo "deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse" > /etc/apt/sources.list
cat /etc/apt/sources.list
ping -c 4 114.114.114.114
apt-get update
$ vagrant up
注意,最好安装salt的最新release版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apt-get install software-properties-common python-software-properties
sudo add-apt-repository ppa:saltstack/salt2015-5
apt-get install -y salt-master
salt --versions-report
Salt: 2015.5.3
Python: 2.7.6 (default, Mar 22 2014, 22:59:56)
Jinja2: 2.7.2
M2Crypto: 0.21.1
msgpack-python: 0.3.0
msgpack-pure: Not Installed
pycrypto: 2.6.1
libnacl: Not Installed
PyYAML: 3.10
ioflo: Not Installed
PyZMQ: 14.0.1
RAET: Not Installed
ZMQ: 4.0.4
Mako: 0.9.1
Tornado: Not Installed
Debian source package: 2015.5.3+ds-1trusty1
at minion node, config salt master host and self id
1
2
3
4
5
$ root@salt-minion:~# vi /etc/salt/minion
# master: **salt-master**
# id: **salt-minion**
$ root@salt-minion:~# service salt-minion restart
at master node, validate salt-key
1
2
3
# list all salt minion keys status
$ root@salt-master:~# salt-key
# show fingerprients
如果/etc/salt/master中auto_accept不是True,那么需要手动签名
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
root@salt-master:~# tree /etc/salt/
/etc/salt/
├── master
├── master.d
│ └── salt-api.conf
└── pki
└── master
├── master.pem
├── master.pub
├── minions
├── minions_autosign
├── minions_denied
├── minions_pre
│ └── salt-minion ### HERE
└── minions_rejected
$ root@salt-master:~# salt-key -f salt-minion
# accept the key at master
$ root@salt-master:~# salt-key -a salt-minion
# delete key
salt-key -d MINION
root@salt-master:~# tree /etc/salt/
/etc/salt/
├── master
├── master.d
│ └── salt-api.conf
└── pki
└── master
├── master.pem
├── master.pub
├── minions
│ └── salt-minion ### HERE
├── minions_autosign
├── minions_denied
├── minions_pre
└── minions_rejected
test it!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ root@salt-master:~# salt '*' test.ping
$ root@salt-master:~# salt 'salt-minion' sys.doc test.ping
test.ping:
Just used to make sure the minion is up and responding
Return True
CLI Example:
salt '*' test.ping
root@salt-master:~# salt 'salt-minion' sys.list_functions | wc -l
603
root@salt-master:~# salt 'salt-minion' sys.list_modules | wc -l
66
Usage: salt [options]
3.1 global matching
target = ‘host-name’
target = ‘’
target = ‘my ’
target = ‘my*client’
target = ‘my??client’
target = ‘my[a-z]client’
3.2 PCRE
target = -E
3.3 list matching
target = -L
3.4 grains
target = –grain ‘os_familiy:RedHat’
target = -G ‘os:Ubuntu’
target = -G ‘os:centos*’
salt ‘*’ grains.items
cat /etc/salt/grains
3.5 pillar
TODO
3.6 compound matching
target = -C 1
'*minion and G@os:Ubuntu and not L@yourminion,theirminion'
target = -C 1
'* and not G@os_family:RedHat'
1
2
3
4
5
6
7
8
Letter Match Type Example
G Grains glob G@os:Ubuntu
E PCRE minion ID E@web\d+\.(dev|qa|prod)\.loc
P Grains PCRE P@os:(RedHat|Fedora|CentOS)
L List of minions L@minion1.example.com, minion3.domain.com
I Pillar glob I@pdata:foobar
S Subnet/IP address S@192.168.1.0/24 or S@192.168.1.100
R Range cluster R@%foo.bar
1
2
3
4
5
6
7
8
salt '*' sys.list_modules
salt '*' sys.doc user.add
salt '*' user.info <User>
salt '*' sys.doc pkg.install
salt '*' pkg.install apache2
salt '*' service.status apache2
salt '*' status.diskusage
salt '*' cmd.run 'echo HELLO'
1
2
git clone https://github.com/saltstack/salt.git
ls -l salt/modules
call = __salt__ [ 'cmd.run_all' ]( cmd , output_loglevel = 'trace' , python_shell = False )
# Define the module's virtual name __virtualname__ = 'pkg' def __virtual__ (): ''' Confirm this module is on a Debian based system ''' if __grains__ . get ( 'os_family' , False ) == 'Kali' : return __virtualname__ elif __grains__ . get ( 'os_family' , False ) == 'Debian' : return __virtualname__ return False
1
# see: salt/modules/config.py
def option ( value , default = '' , omit_opts = False , omit_master = False , omit_pillar = False ): ''' Pass in a generic option and receive the value that will be assigned CLI Example: .. code-block:: bash salt '*' config.option redis.host ''' if not omit_opts : if value in __opts__ : return __opts__ [ value ] if not omit_master : if value in __pillar__ . get ( 'master' , {}): return __pillar__ [ 'master' ][ value ] if not omit_pillar : if value in __pillar__ : return __pillar__ [ value ] if value in DEFAULTS : return DEFAULTS [ value ] return default
5.1 custom moduels
By defualt, custom modeuls path live in
, create it by manual.
The first custom module, eonstack
1
salt '*' eonstack.run_qos
5.1.1 write python code
1
cat /srv/salt/_modules/eonstack.py
# -*- coding: utf-8 -*- from __future__ import absolute_import # Import python libs import os import logging import json # Import third party libs import yaml # Define the module's virtual name __virtualname__ = 'eonstack' def __virtual__ (): ''' Confirm this module is on a Debian based system ''' if __grains__ . get ( 'os_family' , False ) == 'Kali' : return __virtualname__ elif __grains__ . get ( 'os_family' , False ) == 'Debian' : return __virtualname__ return __virtualname__ def echo ( text ): ''' Return a string - used for testing the connection CLI Example: .. code-block:: bash salt '*' test.echo 'foo bar baz quo qux' ''' return text
5.1.2 sync custom modules to all minion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
salt '*' saltutl.sync_util
[root@salt-master _modules]# salt '*' saltutil.sync_all
salt-client1:
----------
beacons:
grains:
modules:
- modules.eonstack
outputters:
renderers:
returners:
states:
utils:
salt-master:
----------
beacons:
grains:
modules:
- modules.eonstack
outputters:
renderers:
returners:
states:
utils:
5.1.3 test it!
1
[root@salt-master _modules]# salt '*' eonstack.echo 'hello world'
By default, states are located in the /srv/salt driectory. All Salt-specific files that aren’t Python files and in the extension .sls .
1
2
3
4
5
6
7
# list all state modules
salt "Minion" sys.list_state_modules
# show state function
salt "Mionion" sys.list_state_functions <STATE>
# help
salt 'Minion' sys.state_doc <STATE>
salt 'Minion' sys.state_doc <STATE.func>
6.1 first example
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
# cat /srv/salt/git.sls
root@node-1:/srv/salt# cat /srv/salt/git.sls
---
install_git:
pkg.installed:
-
name: git
# run it
root@node-1:/srv/salt# salt 'node-3*' state.sls git
node-3.domain.tld:
----------
State: - pkg
Name: git
Function: installed
Result: True
Comment: Package git is already installed
Changes:
Summary
------------
Succeeded: 1
Failed: 0
------------
Total: 1
# remove git package and rerun the states
root@node-1:/srv/salt# salt 'node-3*' pkg.remove git
root@node-1:/srv/salt# salt 'node-3*' state.sls git
node-3.domain.tld:
----------
State: - pkg
Name: git
Function: installed
Result: True
Comment: The following packages were installed/updated: git.
Changes: git: { new : 1:1.9.1-1ubuntu0.1
old :
}
git-core: { new : 1
old :
}
git-completion: { new : 1
old :
}
Summary
------------
Succeeded: 1
Failed: 0
------------
Total: 1
List the functions for a given state module with sys.list_functions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@node-1:/srv/salt# salt 'node-3*' sys.list_functions pkg
node-3.domain.tld:
- pkg.available_version
- pkg.del_repo
- pkg.expand_repo_def
- pkg.file_dict
- pkg.file_list
- pkg.get_repo
- pkg.get_selections
- pkg.install
- pkg.latest_version
- pkg.list_pkgs
- pkg.list_repos
- pkg.list_upgrades
- pkg.mod_repo
- pkg.purge
- pkg.refresh_db
- pkg.remove
- pkg.set_selections
- pkg.upgrade
- pkg.upgrade_available
- pkg.version
- pkg.version_cmp
state declaration is called:
1
2
3
4
5
6
7
8
<ID Declaration>:
<State Module>.<Function>:
- name: <name>
- <Function Arg>
- <Function Arg>
- <Function Arg>
- <Requisite Declaration>:
- <Requisite Reference>
在master结点安装restapi 1
apt-get install -y salt-api
构造ssl key
1
2
openssl genrsa -out /root/salt-api/key.pem 4096
openssl req -new -x509 -key /root/salt-api/key.pem -out /root/salt-api/cert.pem -days 1826
编辑rest server的配置文件如下rest_cherrypy :
1
2
3
4
5
6
7
8
9
root@salt-master:/etc/salt/master.d# cat /etc/salt/master.d/salt-api.conf
rest_cherrypy:
port: 8080
host: 172.16.200.100
ssl_crt: /root/salt-api/cert.pem
ssl_key: /root/salt-api/key.pem
webhook_disable_auth: True
webhook_url: /hook
collect_stats: True
创建API用户
1
2
3
groupadd eonstack
useradd -g eonstack -M -s /user/sbin/nologin -c 'saltstack rest api user for eonboard' eonstack
passwd eonstack
设置salt-api验证用户的类型
1
2
3
4
5
6
7
8
# cat /etc/salt/master
# The external auth system uses the Salt auth modules to authenticate and
# validate users to access areas of the Salt system.
external_auth:
pam:
eonstack:
- .*
重启服务1
service salt-master restart && service salt-api restart
验证API
1
2
3
4
5
6
7
8
9
10
11
url_map = {
'index': LowDataAdapter,
'login': Login,
'logout': Logout,
'minions': Minions,
'run': Run,
'jobs': Jobs,
'keys': Keys,
'events': Events,
'stats': Stats,
}
index(GET/POST)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# GET
curl --insecure https://172.16.200.100:8080/index/ -H 'Accept: application/x-yaml' -d username='eonstack' -d password='password' -d eauth='pam' -X GET
clients:
- _is_master_running
- local
- local_async
- local_batch
- runner
- runner_async
- ssh
- ssh_async
- wheel
- wheel_async
return: Welcome
# POST
curl --insecure https://172.16.200.100:8080/index/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : badf65223c19084464a8e9315e5900a0efc100df' -d client=local -d tgt='*' -d fun=test.ping -X POST
return:
- salt-minion: true
login (GET/POST)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# GET
curl --insecure https://172.16.200.100:8080/login/ -H 'Accept: application/x-yaml' -d username='eonstack' -d password='password' -d eauth='pam' -X GET
return: Please log in
status: null
# POST
curl --insecure https://172.16.200.100:8080/login/ -H 'Accept: application/x-yaml' -d username='eonstack' -d password='password' -d eauth='pam' -X POST
return:
- eauth: pam
expire: 1445633027.415278
perms:
- .*
start: 1445589827.415275
token: badf65223c19084464a8e9315e5900a0efc100df
user: eonstack
logout (POST)
1
2
3
# POST
curl --insecure https://172.16.200.100:8080/logout/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : badf65223c19084464a8e9315e5900a0efc100df' -X POST
return: Your token has been cleared
minions (GET/POST)
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
# GET
curl --insecure https://172.16.200.100:8080/minions/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 1e975aadb3149374c9e6d5c481b925845e736f2c' -X GET
return:
- salt-minion:
grains.items
# GET BY not exist ID
curl --insecure https://172.16.200.100:8080/minions/not-exists/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 1e975aadb3149374c9e6d5c481b925845e736f2c' -X GET
return:
- {}
# GET BY ID
curl --insecure https://172.16.200.100:8080/minions/salt-minion/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 1e975aadb3149374c9e6d5c481b925845e736f2c' -X GET
return:
- salt-minion:
grains.items
# POST
curl --insecure https://172.16.200.100:8080/minions/ -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 1e975aadb3149374c9e6d5c481b925845e736f2c' -X POST -d tgt='*' -d fun='status.diskusage'
_links:
jobs:
- href: /jobs/20151023090633311442
return:
- jid: '20151023090633311442'
minions:
- salt-minion
jobs (GET)
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
# GET JOB
curl --insecure https://172.16.200.100:8080/jobs/20151023092345571347 -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 4b01221fea2d1ec7026cf087a11724dbb7e44017' -X GET
info:
- Arguments: []
Function: status.diskusage
Minions:
- salt-minion
Result:
salt-minion:
return:
/:
available: 38232371200
total: 41612050432
/dev:
available: 1038528512
total: 1038532608
/dev/pts:
available: 0
total: 0
StartTime: 2015, Oct 23 09:23:45.571347
Target: '*'
Target-type: glob
User: eonstack
jid: '20151023092345571347'
return:
- salt-minion:
/:
available: 38232371200
total: 41612050432
/dev:
available: 1038528512
total: 1038532608
/dev/pts:
# GET JOB by not exist id
root@salt-minion:~# curl --insecure https://172.16.200.100:8080/jobs/xxx -H 'Accept: application/x-yaml' -H 'X-Auth-Token : 4b01221fea2d1ec7026cf087a11724dbb7e44017' -X GET
info:
- Arguments: []
Function: unknown-function
Result: {}
StartTime: ''
Target: unknown-target
Target-type: []
User: root
jid: xxx
return:
- {}
run(POST)
need: user/password at the post body every tim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# POST
curl --insecure https://172.16.200.100:8080/run -H 'Accept: application/json' -d username='eonstack' -d password='password' -d eauth='pam' -d client='local' -d tgt='*' -d fun='test.ping'
{"return": [{"salt-minion": true}]}
# POST
curl --insecure https://172.16.200.100:8080/run/ -H 'Accept: application/x-yaml' -d username='eonstack' -d password='password' -d eauth='pam' -d client='local' -d tgt='*' -d fun='test.fib' -d arg=10
return:
- salt-minion:
- - 0
- 1
- 1
- 2
- 3
- 5
- 8
- 4.0531158447265625e-06
events(GET)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 监听事件 - 命令行
$ salt-run state.event pretty=True
# 监听事件 - HTTP Keep-alive
$ curl -G -NsS --insecure https://10.6.14.212:8080/events -H "X-Auth-Token: 7e2a7e82876a36f1b6d4289f2561d80f2ef29ad4"
retry: 400
tag: salt/event/new_client
data: {"tag": "salt/event/new_client", "data": {"_stamp": "2015-11-11T10:14:11.295501"}}
# 监听事件 by python
resp = requests.get("https://10.6.14.212:8080/events", verify=False, headers={'X-Auth-Token': '99c5dc46bdff4c40ca3e31b2f90291fe87518cee'}, stream=True)
for line in resp.iter_lines():
if(line):
print line
reference
SALT TABLE OF CONTENTS