使用salt-api来搭建salt自动化平台

简介:

一、介绍

    通常使用saltstack都是在master的服务器上直接命令操作,这个对于运维人员来说不是什么大事,但是也会有出错的时候,而一旦出错,就会有不可挽回的后果。

二、框架

    这里使用django框架,通过对salt-api的封装,传入命令,执行api,将结果返回到页面上显示。注意:为了防止误操作,我们对传入的命令进行了检查,所有被定义的危险命令将不会被执行。(我这里为了简单,所以定义了可以被执行的命令。),前端使用了jquery+ajax的方式来不刷新页面就将结果显示在页面上的方式。

三、salt-api的安装

    网上教程很多,我这里就不再废话了。

四、django代码

   1)、整体结构

        92d5cb02ff19097972c6a09fe3c6e7ea.png


    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  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 >