一、介绍
通常使用saltstack都是在master的服务器上直接命令操作,这个对于运维人员来说不是什么大事,但是也会有出错的时候,而一旦出错,就会有不可挽回的后果。
二、框架
这里使用django框架,通过对salt-api的封装,传入命令,执行api,将结果返回到页面上显示。注意:为了防止误操作,我们对传入的命令进行了检查,所有被定义的危险命令将不会被执行。(我这里为了简单,所以定义了可以被执行的命令。),前端使用了jquery+ajax的方式来不刷新页面就将结果显示在页面上的方式。
三、salt-api的安装
网上教程很多,我这里就不再废话了。
四、django代码
1)、整体结构
2)、salt_api.py(这里参照了github上dzhops的代码)
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
# -*- coding: utf-8 -*-
import
urllib2, urllib, json
import
requests
import
json
import
ssl
ssl._create_default_https_context
=
ssl._create_unverified_context
class
SaltAPI(
object
):
def
__init__(
self
, url, username, password):
self
.__url
=
url.rstrip(
'/'
)
self
.__user
=
username
self
.__password
=
password
self
.__token_id
=
self
.saltLogin()
def
saltLogin(
self
):
params
=
{
'eauth'
:
'pam'
,
'username'
:
self
.__user,
'password'
:
self
.__password}
encode
=
urllib.urlencode(params)
obj
=
urllib.unquote(encode)
headers
=
{
'X-Auth-Token'
: ''}
url
=
self
.__url
+
'/login'
req
=
urllib2.Request(url, obj, headers)
opener
=
urllib2.urlopen(req)
content
=
json.loads(opener.read())
try
:
token
=
content[
'return'
][
0
][
'token'
]
return
token
except
KeyError:
raise
KeyError
def
postRequest(
self
, obj, prefix
=
'/'
):
url
=
self
.__url
+
prefix
headers
=
{
'X-Auth-Token'
:
self
.__token_id}
req
=
urllib2.Request(url, obj, headers)
opener
=
urllib2.urlopen(req)
content
=
json.loads(opener.read())
return
content
def
masterToMinionContent(
self
, tgt, fun, arg):
'''
Master控制Minion,返回的结果是内容,不是jid;
目标参数tgt是一个如下格式的字符串:'*' 或 'zhaogb-201'
'''
if
tgt
=
=
'*'
:
params
=
{
'client'
:
'local'
,
'tgt'
: tgt,
'fun'
: fun,
'arg'
: arg}
else
:
params
=
{
'client'
:
'local'
,
'tgt'
: tgt,
'fun'
: fun,
'arg'
: arg,
'expr_form'
:
'list'
}
obj
=
urllib.urlencode(params)
content
=
self
.postRequest(obj)
result
=
content[
'return'
][
0
]
return
result
def
allMinionKeys(
self
):
'''
返回所有Minion keys;
分别为 已接受、待接受、已拒绝;
:return: [u'local', u'minions_rejected', u'minions_denied', u'minions_pre', u'minions']
'''
params
=
{
'client'
:
'wheel'
,
'fun'
:
'key.list_all'
}
obj
=
urllib.urlencode(params)
content
=
self
.postRequest(obj)
minions
=
content[
'return'
][
0
][
'data'
][
'return'
][
'minions'
]
minions_pre
=
content[
'return'
][
0
][
'data'
][
'return'
][
'minions_pre'
]
minions_rej
=
content[
'return'
][
0
][
'data'
][
'return'
][
'minions_rejected'
]
# return minions, minions_pre, minions_rej
return
minions
def
actionKyes(
self
, keystrings, action):
'''
对Minion keys 进行指定处理;
:param keystrings: 将要处理的minion id字符串;
:param action: 将要进行的处理,如接受、拒绝、删除;
:return:
{"return": [{"tag": "salt/wheel/20160322171740805129", "data": {"jid": "20160322171740805129", "return": {}, "success": true, "_stamp": "2016-03-22T09:17:40.899757", "tag": "salt/wheel/20160322171740805129", "user": "zhaogb", "fun": "wheel.key.delete"}}]}
'''
func
=
'key.'
+
action
params
=
{
'client'
:
'wheel'
,
'fun'
: func,
'match'
: keystrings}
obj
=
urllib.urlencode(params)
content
=
self
.postRequest(obj)
ret
=
content[
'return'
][
0
][
'data'
][
'success'
]
return
ret
def
acceptKeys(
self
, keystrings):
'''
接受Minion发过来的key;
:return:
'''
params
=
{
'client'
:
'wheel'
,
'fun'
:
'key.accept'
,
'match'
: keystrings}
obj
=
urllib.urlencode(params)
content
=
self
.postRequest(obj)
ret
=
content[
'return'
][
0
][
'data'
][
'success'
]
return
ret
def
deleteKeys(
self
, keystrings):
'''
删除Minion keys;
:param node_name:
:return:
'''
params
=
{
'client'
:
'wheel'
,
'fun'
:
'key.delete'
,
'match'
: keystrings}
obj
=
urllib.urlencode(params)
content
=
self
.postRequest(obj)
ret
=
content[
'return'
][
0
][
'data'
][
'success'
]
return
ret
|
3)、views.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
|
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.shortcuts
import
render
from
django.shortcuts
import
HttpResponse,HttpResponseRedirect,render_to_response
from
models
import
*
from
saltapi
import
salt_api
from
django.http
import
JsonResponse
import
json
def
index(request):
accect
=
[]
context
=
accect_cmd.objects.values()
for
i
in
context:
accect.append(i[
"command"
])
if
request.method
=
=
"POST"
:
key
=
request.POST.get(
'key'
)
cmd
=
request.POST.get(
'cmd'
)
if
cmd.split( )[
0
]
in
accect:
spi
=
salt_api.SaltAPI(
'https://ip:8000'
,
'username'
,
'password'
)
result2
=
spi.masterToMinionContent(key,
'cmd.run'
, cmd)
return
JsonResponse(result2, safe
=
False
)
else
:
data
=
{key:
"请检查命令是否正确或命令超权限,请联系管理员!"
}
return
JsonResponse(data, safe
=
False
)
else
:
return
render_to_response(
'index.html'
)
|
4)、models.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
|
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
# Create your models here.
class
accect_cmd(models.Model):
command
=
models.CharField(max_length
=
50
, unique
=
True
, verbose_name
=
u
'命令'
)
status
=
models.CharField(max_length
=
20
, verbose_name
=
u
'状态'
)
def
__unicode__(
self
):
return
u
'{0} {1}'
.
format
(
self
.command,
self
.status)
class
SaltReturns(models.Model):
fun
=
models.CharField(max_length
=
50
)
jid
=
models.CharField(max_length
=
255
)
return_field
=
models.TextField(db_column
=
'return'
)
success
=
models.CharField(max_length
=
10
)
full_ret
=
models.TextField()
alter_time
=
models.DateTimeField()
class
Meta:
managed
=
False
db_table
=
'salt_returns'
def
__unicode__(
self
):
return
u
'%s %s %s'
%
(
self
.jid,
self
.
id
,
self
.return_field)
class
record(models.Model):
time
=
models.DateTimeField(u
'时间'
, auto_now_add
=
True
)
comment
=
models.CharField(max_length
=
128
, blank
=
True
, default
=
'', null
=
True
, verbose_name
=
u
"记录"
)
def
__unicode__(
self
):
return
u
'%s %s'
%
(
self
.time,
self
.comment)
|
5)、index.html
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
|
<!DOCTYPE html>
<
html
lang
=
"en"
>
<
head
>
<
meta
charset
=
"UTF-8"
>
<
title
>salt平台</
title
>
<
script
src
=
"/static/jquery-2.1.1.min.js"
></
script
>
</
head
>
<
body
>
<
form
action
=
"/salt/index/"
method
=
"POST"
id
=
"form"
>
<
div
>主机:<
input
type
=
"text"
name
=
"key"
value
=
""
id
=
"a"
style
=
"width: 200px"
></
div
>
<
div
>命令:<
input
type
=
"text"
name
=
"cmd"
value
=
""
id
=
"b"
style
=
"width: 200px"
></
div
>
<
div
><
button
type
=
"button"
id
=
"fb"
>执行</
button
></
div
>
<
div
style
=
"height: 300px;margin-top: 15px;"
>
<
textarea
type
=
"text"
style
=
"width: 60%;height: 300px"
disabled
=
"disabled"
class
=
"left"
name
=
"comment"
id
=
"c"
></
textarea
>
</
div
>
</
form
>
</
body
>
<
script
>
$("#fb").click(function () {
$.post("/salt/index/",{
key:$("#a").val(),
cmd: $("#b").val(),
},
function (response,status,xhr) {
$("#c").html('')
$.each(response,function (key,val) {
var c = "\r\n"+key+ ":\r\n" + val;
$("#c").append(c);
})
}
)
})
</
script
>
</
html
>
|
五、效果
1)、单个key执行
2)、多个key执行
3)、当命令不被许可时:
六、总结
写的比较简陋,而且现在这个版本并不支持类似于192.168.1.1+,192.168.1.*这种正则匹配,后续会继续增加。
本文转自 sykmiao 51CTO博客,原文链接:http://blog.51cto.com/syklinux/1981943,如需转载请自行联系原作者