SaltApi

class SaltAPI(object):

    def __init__(self, url=None, user=None, password=None):
        self.__url = settings.SALT_API_URL
        self.__user = settings.SALT_API_USER
        self.__password = settings.SALT_API_PASSWORD
        self.__headers = {'Accept': 'application/json', 'Content-type': 'application/json', 'Connection': 'close'}
        self.__data = {'client': 'local'}
        self.__token = None

    def get_token(self):
        """
        用户登陆和获取token
        :return:
        """
        params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
        content = self.postRequest(params, self.__headers, prefix='login')
        try:
            self.__token = content['return'][0]['token']
            self.__headers['X-Auth-Token'] = self.__token
        except Exception as e:
            logger.error(e)
            return content

    def get_grains(self, target=None):
        """
        获取系统基础信息
        :return:
        """
        data = copy.deepcopy(self.__data)
        if target:
            data['tgt'] = target
        else:
            data['tgt'] = '*'
        data['fun'] = 'grains.items'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def get_auth_keys(self):
        """
        获取所有已认证的主机
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'wheel'
        data['fun'] = 'key.list_all'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]['data']['return']['minions']
        except Exception as e:
            logger.error(e)
            return content

    def get_minion_status(self):
        """
        获取所有主机的连接状态
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'manage.status'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def delete_key(self, minion=None):
        '''
        删除指定主机的认证信息
        '''
        if not minion:
            return {'success': False, 'msg': 'minion-id is none'}

        data = copy.deepcopy(self.__data)
        data['client'] = 'wheel'
        data['fun'] = 'key.delete'
        data['match'] = minion
        content = self.postRequest(data, self.__headers)

        try:
            return {'success': content['return'][0]['data']['success']}
        except Exception as e:
            logger.error(e)
            return content

    def minion_alive(self, minion=None):
        '''
        Minion主机存活检测
        '''
        data = copy.deepcopy(self.__data)
        if minion:
            data['tgt'] = minion
            result = {minion: False}
        else:
            data['tgt'] = '*'
            result = {'success': False}
        data['fun'] = 'test.ping'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return result

    def passwd(self, target=None, user=None, password=None, pass_length=16):
        """
        修改密码
        :param target: 目标客户端
        :param user: 目标客户端的系统用户名
        :param password: 新的密码,必须大于等于12位
        :return:
        """
        if not target:
            return {'success': False, 'msg': 'target is none.'}, password
        if not user:
            return {'success': False, 'msg': 'user is none.'}, password
        if password:
            if len(password) < pass_length:
                return {'success': False, 'msg': 'password must be greater than or equal to {} bits.'.format(pass_length)}, password

            if password.isalpha() or password.isdigit() or password.islower() or password.isupper():
                return {'success': False, 'msg': 'password must be have lowercase, uppercase and digit.'}, password
        else:
            password = make_pass(pass_length)

        _password = crypt(password, 'cmdb')
        try:
            self.cmd(target=target, arg='usermod -p "{}" {}'.format(_password, user))
        except Exception as e:
            logger.debug(e)

        res = {'success': True,
               'msg': 'Changing password for user {}.all authentication tokens updated successfully.'.format(user),
        }

        return res, password

    def get_users(self, target=None):
        """
        获取系统用户
        :param target: 目标客户端
        :return:
        """
        if not target: return {'success': False, 'msg': 'target is none.'}
        content = self.cmd(target=target, arg="grep /bin/bash /etc/passwd|awk -F ':' '{print $1}'")
        return content

    def run_cmdb_agent(self, target=None):
        """
        运行cmdb agent
        :param target: 目标客户端
        :return:
        """
        if not target:
            return {'success': False, 'msg': 'target is none.'}
        content = self.cmd(target=target, arg="/etc/init.d/vmagent")
        return content

    def cmd(self, target=None, fun='cmd.run', arg=None, async=False):
        """
        远程执行任务
        :param target: 目标客户端,为空return False
        :param fun: 模块
        :param arg: 参数,可为空
        :param async: 异步执行,默认非异步
        :return:
        """
        data = copy.deepcopy(self.__data)
        if not target:
            return {'success': False, 'msg': 'target is none'}
        if arg:
            data['arg'] = arg
        if async:
            data['client'] = 'local_async'
        data['tgt'] = target
        data['fun'] = fun

        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def jobs(self, fun=None, jid=None):
        """
        任务
        :param fun: active,detail
        :param jid: Job ID
        :return:
        """
        data = {'client': 'runner'}
        if fun == 'active':
            data['fun'] = 'jobs.active'
        elif fun == 'detail':
            if not jid: return {'success': False, 'msg': 'job id is none'}
            data['fun'] = 'jobs.lookup_jid'
            data['jid'] = jid
        else:
            return {'success': False, 'msg': 'fun is active or detail'}
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def postRequest(self, data, headers, prefix=None):
        if prefix:
            url = '{}/{}'.format(self.__url, prefix)
        else:
            url = self.__url
        try:
            s = requests.Session()
            s.mount('https://', HTTPAdapter(max_retries=10))
            ret = s.post(url, data=json.dumps(data), headers=headers, verify=False, timeout=(30, 60))
            if ret.status_code == 401:
                logger.error('Salt Unauthorized')
                return {'return': [{'success': False, 'msg': 'Salt Unauthorized'}]}
            elif ret.status_code == 200:
                return ret.json()
            else:
                return {'return': [{'success': False, 'msg': ret.content}]}
        except Exception as e:
            logger.error(e)
            return {'return': [{'success': False, 'msg': e}]}

    def get_pre_auth_keys(self):
        """
        获取未授权的salt主机
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'wheel'
        data['fun'] = 'key.list_all'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]['data']['return']['minions_pre']
        except Exception as e:
            logger.error(e)
            return content

    def accept_key(self, minion):
        """
        授权salt主机
        :param minion:
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'wheel'
        data['fun'] = 'key.accept'
        data['match'] = minion
        content = self.postRequest(data, self.__headers)

        try:
            return {'success': content['return'][0]['data']['success']}
        except Exception as e:
            logger.error(e)
            return content

    def salt_alive(self, tgt):
        '''
        Minion主机存活检测
        '''
        data = copy.deepcopy(self.__data)
        if tgt:
            data['tgt'] = tgt
        else:
            data['tgt'] = '*'
        data['fun'] = 'test.ping'
        content = self.postRequest(data, self.__headers)
        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return None

    def getRequest(self, headers, prefix=None):
        if prefix:
            url = '{}/{}'.format(self.__url, prefix)
        else:
            url = self.__url
        try:
            s = requests.Session()
            s.mount('https://', HTTPAdapter(max_retries=10))
            ret = s.get(url, headers=headers, verify=False, timeout=(30, 60))
            if ret.status_code == 401:
                logger.error('Salt Unauthorized')
                return {'return': [{'success': False, 'msg': 'Salt Unauthorized'}]}
            elif ret.status_code == 200:
                return ret.json()
        except Exception as e:
            logger.error(e)
            return {'return': [{'success': False, 'msg': e}]}

    def salt_runner_requests(self, jid):
        '''
        通过jid获取执行结果
        '''

        content = self.getRequest(prefix='/jobs/{}'.format(jid), headers=self.__headers)
        return content

    def salt_runner(self, jid):
        """
        获取job的执行结果
        :param jid:
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'jobs.lookup_jid'
        data['jid'] = jid
        content = self.postRequest(data, self.__headers)
        return content

    def salt_running_jobs(self):
        """
        获取在运行的job
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['clent'] = 'runner'
        data['fun'] = 'jobs.active'
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def remote_execution(self, tgt, fun, arg, expr_form):
        """
        异步执行远程指令
        :param tgt:
        :param fun:
        :param arg:
        :param expr_form:
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'local_async'
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg
        data['expr_form'] = expr_form

        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]['jid']
        except Exception as e:
            logger.error(e)
            return content

    def remote_module(self, tgt, fun, arg, kwarg, expr_form, client='local_async'):
        """
        异步部署模块
        :param tgt:
        :param fun:
        :param arg:
        :param kwarg:
        :param expr_form:
        :param client local_async 异步 or local 同步
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = client
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg
        data['kwarg'] = {"pillar": kwarg}
        data['expr_form'] = expr_form

        content = self.postRequest(data, self.__headers)
        if client == "local_async":
            try:
                return content['return'][0]['jid']
            except Exception as e:
                logger.error(e)
                return content
        else:
            return content

    def remote_localexec(self, tgt, fun, arg, expr_form):
        data = copy.deepcopy(self.__data)
        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg
        data['expr_form'] = expr_form

        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]['jid']
        except Exception as e:
            logger.error(e)
            return content

    def salt_state(self, tgt, arg, expr_form):
        """
        sls文件
        :param tgt:
        :param arg:
        :param expr_form:
        :return:
        """

        data = copy.deepcopy(self.__data)
        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = 'state.sls'
        data['arg'] = arg
        data['expr_form'] = expr_form
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def project_manage(self, tgt, fun, arg1, arg2, arg3, arg4, arg5, expr_form):
        """
        项目管理
        :param tgt:
        :param fun:
        :param arg1:
        :param arg2:
        :param arg3:
        :param arg4:
        :param arg5:
        :param expr_form:
        :return:
        """
        data = copy.deepcopy(self.__data)

        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg1
        data['arg2'] = arg2
        data['arg3'] = arg3
        data['arg4'] = arg4
        data['arg5'] = arg5
        data['expr_form'] = expr_form
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def file_copy(self, tgt, fun, arg1, arg2, expr_form):
        """
        文件copy
        :param tgt: ./
        :param fun: file.manager
        :param arg1:
        :param arg2:
        :param expr_form:
        :return:
        """
        data = copy.deepcopy(self.__data)

        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg1
        data['arg2'] = arg2
        data['expr_form'] = expr_form
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def file_bak(self, tgt, fun, arg, expr_form):
        """
        文件备份到master上
        :param tgt:
        :param fun:
        :param arg:
        :param expr_form:
        :return:
        """
        data = copy.deepcopy(self.__data)

        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        data['expr_form'] = expr_form
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def file_manage(self, tgt, fun, arg1, arg2, arg3, expr_form):
        """
        文件回滚
        :param tgt:
        :param fun:
        :param arg1:
        :param arg2:
        :param arg3:
        :param expr_form:
        :return:
        """
        data = copy.deepcopy(self.__data)

        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        data['arg'] = arg1
        data['arg2'] = arg2
        data['arg3'] = arg3
        data['expr_form'] = expr_form
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0]
        except Exception as e:
            logger.error(e)
            return content

    def remote_server_info(self, tgt, fun):
        """
        获取远程主机信息
        :param tgt:
        :param fun:
        :return:
        """
        data = copy.deepcopy(self.__data)

        data['client'] = 'local'
        data['tgt'] = tgt
        data['fun'] = fun
        content = self.postRequest(data, self.__headers)

        try:
            return content['return'][0][tgt]
        except Exception as e:
            logger.error(e)
            return content

    def get_list_job(self):
        """
        获取正在运行的job
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'jobs.list_jobs'
        content = self.postRequest(data, self.__headers)
        ret = content['return'][0]
        return [{k: v} for k, v in zip(ret.keys(), ret.values())]

    def get_running_job(self):
        """
        获取正在运行的job
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'jobs.active'
        content = self.postRequest(data, self.__headers)

        ret = content['return'][0]
        return [{k: v} for k, v in zip(ret.keys(), ret.values())]

    def term_running_job(self, jid):
        """
        终止正在运行的job
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['tgt'] = '*'
        data['client'] = 'local'
        data['fun'] = 'saltutil.term_job'
        data['arg'] = jid

        content = self.postRequest(data, self.__headers)

        return content['return'][0]

    def get_job_info(self, jid):
        """
        获取正在运行的job
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'jobs.lookup_jid'
        data['arg'] = jid

        content = self.postRequest(data, self.__headers)

        return content

    def check_job_result(self, jid):
        """
        检查job是否已经运行完成
        :return:
        """
        data = copy.deepcopy(self.__data)
        data['client'] = 'runner'
        data['fun'] = 'jobs.exit_success'
        data['arg'] = jid

        content = self.postRequest(data, self.__headers)

        return content

使用方式

client = SaltAPI()
client.get_token()
然后用client调用方法

异步执行模块部署

入参
module.module_path # 模块路径
  "tgt_list": [
        "SHTL00706921"
    ],
    "arg": "init.6_env_init"
expr_form = 'list'
调用并返回jid
jid = sapi.remote_module(tgt_select, 'state.sls', 'module.{}.{}'.format(module.module_path, module.module),
                                     {'SALTSRC': 'module/{}'.format(module.module_path)}, expr_form)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 最近再看阮一峰的一篇博客提到了一本书《Software Architecture Patterns》(PDF),写...
    卓_然阅读 7,682评论 0 22
  • 先保存起来免得地址失效https://nqdeng.github.io/7-days-nodejs/#6[http...
    Iterate阅读 1,060评论 0 10
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 本文是基于“微服务架构设计模式”这本书的总结和提炼,将其中的关键知识点结合个人的开发实践进行结合提炼,并对部分话题...
    彦帧阅读 4,203评论 1 2
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,548评论 0 11