如今終於開始學習salt的api了,有些小激動啊,咱們執行命令的時候,後臺究竟是如何處理的,發生什麼了事情,我對着一切有着強烈的好奇心啊。python
這些是saltstack命令對應的api: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。app
首先咱們以一個簡單的命令去講解salt是如何處理。ssh
salt '*' test.ping異步
這個首先會調用salt.cli.SaltCMD類實例化一個對象socket
主要代碼以下:salt/cli/__init__.pyasync
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方法。tcp
那麼真正處理操做的是LocalClient類,LocalClient主要包含的方法是cmd_*,get_*_returns,gather_job_info,run_job,pub。ide
首先學習下它怎麼使用吧。使用的是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的流程,相似於卸妝的過程,看到最後的面目。
一、調用SaltCMD處理命令行參數,決定調用LocalClient的哪一個方法(默認是cmd_cli)
二、調用LocalClient處理
三、默認調用cmd_cli方法處理
四、調用run_job方法處理
五、調用pub方法處理
六、使用zmq req/req socket通訊
七、調用gather_job_info觸發saltutil.find_job任務
八、一層層返回結果
從這個流程可知,其實最終調用的就是pub方法。
local.cmd_async('192.168.110.132','test.ping') local.pub('192.168.110.132','test.ping')
salt命令行的API的講解就到這裏。