千家信息网

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

发表于:2024-12-12 作者:千家信息网编辑
千家信息网最后更新 2024年12月12日,一、介绍通常使用saltstack都是在master的服务器上直接命令操作,这个对于运维人员来说不是什么大事,但是也会有出错的时候,而一旦出错,就会有不可挽回的后果。二、框架这里使用django框架,
千家信息网最后更新 2024年12月12日使用salt-api来搭建salt自动化平台

一、介绍

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

二、框架

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

三、salt-api的安装

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

四、django代码

1)、整体结构


2)、salt_api.py(这里参照了github上dzhops的代码)

# -*- coding: utf-8 -*-import urllib2, urllib, jsonimport requestsimport jsonimport sslssl._create_default_https_context = ssl._create_unverified_contextclass 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

# -*- coding: utf-8 -*-from __future__ import unicode_literalsfrom django.shortcuts import renderfrom django.shortcuts import HttpResponse,HttpResponseRedirect,render_to_responsefrom models import *from saltapi import salt_apifrom django.http import JsonResponseimport jsondef 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

# -*- coding: utf-8 -*-from __future__ import unicode_literalsfrom 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

    salt平台  
主机:
命令:

五、效果

1)、单个key执行

2)、多个key执行

3)、当命令不被许可时:

六、总结

写的比较简陋,而且现在这个版本并不支持类似于192.168.1.1+,192.168.1.*这种正则匹配,后续会继续增加。

0