salt的api學習記錄--salt命令的執行過程

現在終於開始學習salt的api了,有些小激動啊,我們執行命令的時候,後臺到底是如何處理的,發生什麼了事情,我對着一切有着強烈的好奇心啊。


這些是saltstack命令對應的api:

salt 		    --->salt.client.LocalClient 
salt-minion     --->salt.minion.Minion	
salt-cp 	    --->salt.cli.cp.SaltCP 			
salt-key	    --->salt.key.KeyCLI				
salt-call	    --->salt.cli.caller.caller		
salt-run	    --->salt.runner.Runner 			
salt-ssh	    --->salt.client.ssh.SSH


首先學習的是salt命令對應的api:salt.client.LocalClient。

首先我們以一個簡單的命令去講解salt是如何處理。

salt '*' test.ping

這個首先會調用salt.cli.SaltCMD類實例化一個對象

主要代碼如下:salt/cli/__init__.py

class SaltCMD(parsers.SaltCMDOptionParser):
    def run(self):
        ...    # 設置日誌的
        # 調用salt.client.LocalClient api
        try:
            local = salt.client.LocalClient(self.get_config_file_path())
        except SaltClientError as exc:
            self.exit(2, '{0}\n'.format(exc))
            return
         
        # self.options是命令行參數的集合
        # 通過optparse模塊解析命令行參數
        # 批量執行的,salt -b
        if self.options.batch:
            batch = salt.cli.batch.Batch(self.config)
            # Printing the output is already taken care of in run() itself
            for res in batch.run():
                pass
         else:
             # 處理需要傳遞的參數,kwargs是參數字典,傳遞時使用**kwargs
             ....
             
             # 異步執行,不等待結果返回
            if self.config['async']:
                jid = local.cmd_async(**kwargs) # 返回jid
                print('Executed command with job ID: {0}'.format(jid))
                return
            try:
                # 接下來是主要邏輯
                # 默認調用cmd_cli方法返回生成器,循環輸出結果
                # 在cmd_cli裏有個get_cli_event_returns方法會去調用gather_job_info
                # gather_job_info是去觸發saltutil.find_job任務
                # 是去驗證操作是否還在執行
                if local:
                    if self.options.subset: # 選擇幾個Minion調用cmd_cli
                        cmd_func = local.cmd_subset
                        kwargs['sub'] = self.options.subset
                        kwargs['cli'] = True
                    else:
                        cmd_func = local.cmd_cli

                    if self.options.static:
                        if self.options.verbose:
                            kwargs['verbose'] = True
                        full_ret = local.cmd_full_return(**kwargs)
                        ret, out = self._format_ret(full_ret)
                        self._output_ret(ret, out)
                    elif self.config['fun'] == 'sys.doc':
                        ret = {}
                        out = ''
                        for full_ret in local.cmd_cli(**kwargs):
                            ret_, out = self._format_ret(full_ret)
                            ret.update(ret_)
                        self._output_ret(ret, out)
                    else:
                        if self.options.verbose:
                            kwargs['verbose'] = True
                        for full_ret in cmd_func(**kwargs):
                            ret, out = self._format_ret(full_ret)
                            self._output_ret(ret, out)

從上面代碼片段我們知道SaltCMD主要是解析命令行參數,通過參數來決定調用salt.client.LocalClient的哪個方法,默認是調用cmd_cli方法。

那麼真正處理操作的是LocalClient類,LocalClient主要包含的方法是cmd_*,get_*_returns,gather_job_info,run_job,pub。

首先學習下它怎麼使用吧。使用的是ipython學習的

:import salt.client
# 初始化實例,參數必須是master的配置文件路徑
:local = salt.client.LocalClient('/opt/app/salt/etc/master')
# cmd(tgt,fun,arg,expr_form='glob',...)
:local.cmd('192.168.110.132','test.ping')
#返回的結果
: {'192.168.110.132': True}
# local.cmd_async('192.168.110.132','test.ping')
# 返回結果是個jid
: '20140813164423244819'

cmd代碼片段如下:

def cmd( 
            self,
            tgt,     # salt target '192.168.110.132' 'os:Linux'
            fun,     # 執行模塊 test.ping
            arg=(),  # 模塊參數
            timeout=None,
            expr_form='glob', # target的類型,
            ret='',    # returners
            kwarg=None,
            **kwargs):
     arg = condition_kwarg(arg, kwarg) # 將參數合併成一個列表
     # run_job返回的結果形式:
     #{'jid': '20131219215650131543', 'minions': ['jerry','sdfsd',]}
     pub_data = self.run_job(tgt,
        fun,
        arg,
        expr_form,
        ret,
        timeout,
        **kwargs)
     if not pub_data:
        return pub_data
     
     # 需要等待timeout時間纔會返回結果
     return self.get_returns(pub_data['jid'],
        pub_data['minions'],
        self._get_timeout(timeout))

其他的cmd_*形式都相似的,都會調用run_job方法,只不過返回結果不一樣而已,比如cmd_async是異步執行的,不用等待,直接返回一個jid。cmd_cli返回的是生成器並會調用

gather_job_info方法去觸發saltutil.find_job任務。cmd_cli是默認的處理方法


run_job代碼如下:

# run_job返回的結果形式:
# {'jid': '20131219215650131543', 'minions': ['jerry','sdfsd',]}
def run_job(
            self,
            tgt,
            fun,
            arg=(),
            expr_form='glob',
            ret='',
            timeout=None,
            kwarg=None,
            **kwargs):
     arg = condition_kwarg(arg, kwarg)
     jid = '' # 這個可以自定義jid

     # Subscribe to all events and subscribe as early as possible
     self.event.subscribe(jid)    # 訂閱event

     pub_data = self.pub(
            tgt,
            fun,
            arg,
            expr_form,
            ret,
            jid=jid,
            timeout=self._get_timeout(timeout),
            **kwargs)

      return self._check_pub_data(pub_data)


pub代碼片段如下:

#payload_kwargs信息如下:
{
'cmd': 'publish',
'tgt': tgt,
'fun': fun,
'arg': arg,
'key': self.key,
'tgt_type': expr_form,
'ret': ret,
'jid': jid,
'user':'root'
}
master_uri = 'tcp://' + salt.utils.ip_bracket(self.opts['interface']) + \
    ':' + str(self.opts['ret_port'])
sreq = salt.transport.Channel.factory(self.opts, crypt='clear', master_uri=master_uri)

# 使用zeromq通信,使用的是rep/req socket
# payload信息
#{'enc': 'clear',
# 'load': {'jid': '20140807004713142582',
#  'minions': ['192.168.79.47', '192.168.110.132', '192.168.110.133']}}

try:
    payload = sreq.send(payload_kwargs)
except SaltReqTimeoutError:
      log.error(
           'Salt request timed out. If this error persists, '
            'worker_threads may need to be increased.'
       )
      return {}


講了這麼多,現在總結下流程。

salt '*' test.ping的流程,類似於卸妝的過程,看到最後的面目。

1、調用SaltCMD處理命令行參數,決定調用LocalClient的哪個方法(默認是cmd_cli)

2、調用LocalClient處理

3、默認調用cmd_cli方法處理

4、調用run_job方法處理

5、調用pub方法處理

6、使用zmq req/req socket通信

7、調用gather_job_info觸發saltutil.find_job任務

8、一層層返回結果


從這個流程可知,其實最終調用的就是pub方法。

local.cmd_async('192.168.110.132','test.ping')
local.pub('192.168.110.132','test.ping')


salt命令行的API的講解就到這裏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章