设为首页 收藏本站
查看: 1971|回复: 0

[经验分享] 探索ansible runner的源码及执行api原理

[复制链接]

尚未签到

发表于 2018-7-30 13:18:33 | 显示全部楼层 |阅读模式
  前沿:
  ansible的文档说的不清不楚,文档一点也不实在,有些范例都做不通,走不通。今天中午吃饭的时候,和同事 祖天彪(这名够霸道吧),聊了很长时间ansible在实际项目中碰到的问题,尤其是运维平台页面上。  下午的时候,找他去玩耍,这哥们正好在看ansible的api源码,也就是 ansible runner ~    就这样一块交流一两个小时,把runner 这部分的核心代码给过了一遍。 中文的文档真的很少, 我这边就简单的阐述下,看完后的一些总结。 可能有些地方不对,或者是没有说明白,还请大家见谅。
import ansible.runner  
runner = ansible.runner.Runner(
  
   host_list="/etc/ansible/xiaorui.py"
  
   module_name='command',
  
   module_args='ip a',
  
   pattern='web',
  
)
  

  
datastructure = runner.run()
  
print '\n'
  
print '\n'
  
print datastructure
  上面是一个简单的ansible api的执行的例子。 我们可以看到他调用的runner模块。
  runner目录下面有个__init__.py文件,__init__.py的作用, 相当于class中的def __init__(self):函数,用来初始化模块。 把所在目录当作一个package处理。不懂的去看,python包相关的基础。
  原文:http://rfyiamcool.blog.51cto.com/1030776/1420147
  下面讲解下 Runner这个类。
  class Runner(object):
  ''' core API interface to ansible '''
  # see bin/ansible for how this is used...
  def __init__(self,
  host_list=C.DEFAULT_HOST_LIST,      这里不仅可以放 静态的hosts文件,也可以放 inventory的脚本,脚本要777权限。
  module_path=None,               这个是ansible的路径,一般不用写
  module_name=C.DEFAULT_MODULE_NAME,      模块的名字,模块的位置要选定在/usr/share/absible下,不然会识别不到。   要注意下~
  module_args=C.DEFAULT_MODULE_ARGS,  # ex: "src=/tmp/a dest=/tmp/b"   模块的参数
  forks=C.DEFAULT_FORKS,              # parallelism level   进程的数目,他的逻辑是这样,你如果填入了20个进程,他会判断你的list_hosts是否有20个,没有的话,他就会根据主机的数目来派生进程,如果超过20个,那就用multiprocess进程池pool来调度。mulitiprocess本身有个isalive的东西,来判断分离出去进程的存活状态。
  timeout=C.DEFAULT_TIMEOUT,       这个就是超时的时间
  pattern=C.DEFAULT_PATTERN,          # which hosts?  ex: 'all', 'acme.example.org'   这个是做相关的匹配,是关于inventory的匹配
  remote_user=C.DEFAULT_REMOTE_USER,  # ex: 'username'  远端用户的选择
  remote_pass=C.DEFAULT_REMOTE_PASS,  # ex: 'password123' or None if using key 远端密码的选择
  remote_port=None,                   # if SSH on different ports   远端端口的选择
  private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords  还可以用指定key
  sudo_pass=C.DEFAULT_SUDO_PASS,      # ex: 'password123' or None   sudo之后的密码的推送
  background=0,                       # async poll every X seconds, else 0 for non-async  看字眼就知道他是做什么的了,他非常的像 saltstack的 event, 当派生出了一个任务后,
  产生一个ansible_job_id,然后时不时的去拿数据
  transport=C.DEFAULT_TRANSPORT,      # 'ssh', 'paramiko', 'local'  这里是选择你的链接得到方式,默认是用的 paramiko
  conditional='True',                 # run only if this fact expression evals to true     这个是什么呢?  相当与 puppet saltstack 里面的require,状态的判断。
  callbacks=None,                     # used for output  回调的输出
  sudo=False,                         # whether to run sudo or not 是否是sudo
  sudo_user=C.DEFAULT_SUDO_USER,      # ex: 'root'  sudo的时候,用到的用户名
  ):
  再来关注下这个,我和同事的关注点,不太一样,他以前经常用salt,他不想写hosts文件,或者是inventory文件,想直接 ansible  ip地址 -m shell "ip a" 类似这样的使用,而我的想法是,想给inventory传递参数,  inventory在被ansbile调用的时候,是不能传递参数的, 他后面的那个参数,只是获取 json的那个key。
  简单的说, ansible -i nima.py "bj_nginx" -m shell "dir"  这里的bj_nginx 是不能传递给nima里面的,而是获取nima返回的那片json的。
self.inventory        = utils.default(inventory, lambda: ansible.inventory.Inventory(host_list))  咱们进到,inventory的目录
  [root@devops-ruifengyun inventory ]$ pwd
  /usr/lib/python2.7/dist-packages/ansible/inventory
  试图找下,官网支持不支持针对inventory的传递参数,host_list传递给了 Inventory类,他的逻辑也很干练,如果 host_list  如果没有传入的话,默认走的是 /etc/ansible/hosts文件,如果你指定了,他会从你的文件读入的,  首先如果是str,根据逗号来区分并且去除空格,放到list里面。接着是list了,。。。  霹雳拉ye,oh。。
  原文:http://rfyiamcool.blog.51cto.com/1030776/1420147
      if type(host_list) in [ str, unicode ]:  
          if host_list.find(",") != -1:
  
              host_list = host_list.split(",")
  
              host_list = [ h for h in host_list if h and h.strip() ]
  

  
      if type(host_list) == list:
  
          self.parser = None
  
          all = Group('all')
  
          self.groups = [ all ]
  
          for x in host_list:
  
              if x.find(":") != -1:
  
                  tokens = x.split(":",1)
  
                  all.add_host(Host(tokens[0], tokens[1]))
  
              else:
  
                  all.add_host(Host(x))
  
      elif os.path.exists(host_list):
  
          if os.path.isdir(host_list):
  
              # Ensure basedir is inside the directory
  
              self.host_list = os.path.join(self.host_list, "")
  
              self.parser = InventoryDirectory(filename=host_list)
  
              self.groups = self.parser.groups.values()
  
          elif utils.is_executable(host_list):
  
              self.parser = InventoryScript(filename=host_list)
  
              self.groups = self.parser.groups.values()
  
          else:
  
              data = file(host_list).read()
  
              if not data.startswith("---"):
  
                  self.parser = InventoryParser(filename=host_list)
  
                  self.groups = self.parser.groups.values()
  
              else:
  
                  raise errors.AnsibleError("YAML inventory support is deprecated in 0.6 and removed in 0.7, see the migration script in examples/scripts in the git checkout")
  

  
          utils.plugins.vars_loader.add_directory(self.basedir(), with_subdir=True)
  
      else:
  
          raise errors.AnsibleError("Unable to find an inventory file, specify one with -i ?")
  

  
  def _match(self, str, pattern_str):
  
  原文:http://rfyiamcool.blog.51cto.com/1030776/1420147
  最后,咱们的流程跑到这里。。。
  elif utils.is_executable(host_list):
  self.parser = InventoryScript(filename=host_list)
  self.groups = self.parser.groups.values()
  咱们再来看看InventoryScript做了什么东西。。。。
DSC0000.jpg

  找到了,  这就是我给 host_list传递字符串的时候,会提示没有该文件,因为他的subprocess的shell状态是 False,这样的话,不能识别 分号和空格。 只要在subprocess.Popen追加一个shell=True就可以了。
  既然ansible runner代码,基本过了一遍,那么 inventory 模块的参数传递,很明了了。。。
  原文:http://rfyiamcool.blog.51cto.com/1030776/1420147
DSC0001.jpg

30     def __init__(self, filename=C.DEFAULT_HOST_LIST):  
31
  
32         self.filename = filename
  
33         cmd = [ self.filename, "--list" ]
  
34         try:
  
35             sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  
36         except OSError, e:
  
37             raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e))
  
38         (stdout, stderr) = sp.communicate()
  
39         self.data = stdout
  
40         self.groups = self._parse()
  接着,他用调用了他自己类下的 _parse() 函数 ~
def _parse(self):  

  
    all_hosts = {}
  
    self.raw  = utils.parse_json(self.data)
  
    all       = Group('all')
  
    groups    = dict(all=all)
  
    group     = None
  

  
    if 'failed' in self.raw:
  
        raise errors.AnsibleError("failed to parse executable inventory script results")
  

  
    for (group_name, data) in self.raw.items():
  

  
        group = groups[group_name] = Group(group_name)
  
        host = None
  

  
        if not isinstance(data, dict):
  
            data = {'hosts': data}
  

  
        if 'hosts' in data:
  

  
            for hostname in data['hosts']:
  
                if not hostname in all_hosts:
  
                    all_hosts[hostname] = Host(hostname)
  
                host = all_hosts[hostname]
  
                group.add_host(host)
  

  
        if 'vars' in data:
  
            for k, v in data['vars'].iteritems():
  
                group.set_variable(k, v)
  
        all.add_child_group(group)
  

  
    # Separate loop to ensure all groups are defined
  
    for (group_name, data) in self.raw.items():
  
        if isinstance(data, dict) and 'children' in data:
  
            for child_name in data['children']:
  
                if child_name in groups:
  
                    groups[group_name].add_child_group(groups[child_name])
  
    return groups
  上面的代码已经很好理解了,就是把刚才通过subprocess获取的数据,用json模块,json.loads一下。这里会取出两个大key,一个是主机类型标签的hosts 这个key,和vars ,这个vars是给模板或者其他功能的数据。
  (上次咱们提过的,ansible 通过cmdb,获取inventory的数据  http://rfyiamcool.blog.51cto.com/1030776/1416808  )
  有很多人会说,这也太搓了,太scha了,居然是通过外部调用获取的数据,但是我觉得这也是很多人着迷ansible的地方,不管你会不会python,你只要会任何一个语言,shell、php、java、nodejs、ruby只要能标准的stdout输出,那就行了。
  剩下的不需要你再度的介入,因为对于ansible来说,我拿到的数据都是标准的输出,所以大家不管用怎么语言,一定要做好异常的处理,让他print干干净净的数据,这样ansible 这个小孩才能完美的加载数据。
  希望大家通过这篇文章,对于ansible api更有深度的了解,一些的帮助,如果文章描述的有问题,请即使的通知我,我会即使的更正修改。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-543637-1-1.html 上篇帖子: ansible学习笔记(三)初始化操作系统:ansible变量使用 下篇帖子: 关于ansible基础入门和功能实现教程的更新页面
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表