原文地址: http://blog.toast38coza.me/custom-ansible-module-hello-world/
什么是 ansible module?Ansible modules 是一种使用python写成的一些功能块,可以在yaml文件中调用,也就是playbook中可以调用的模块,例如常见的模块 copy , debug
Ansible 本身就提供了很多很多的模块 地址
什么时候和什么情况下我们会需要自己写module?绝大多数情况下,我们不需要创建自己的模块module,然而,有些时候官方提供的模块无法提供我们需要的功能,我们只能通过自己的模块来处理一些代码
通常,module和哪些提供了丰富api接口的服务对接起来会更加流畅,例如Github 或者 Pivotal , 当然我们可以通过 url 来和这些服务进行交互,但是,有时候这个方法太不cool 了,我们要牛B的方法
创建一个简单的例子幸运的是创建一个ansible module是非常容易的
我们将创建一个简单的例子:从github上删除或者创建一个repo
我们先来一个简单的输出的例子
创建如下文件:
play.yml [library] |_ github_repo.py |_ test_github_repo.py library/github_repo.pyPython
#!/usr/bin/python fromansible.module_utils.basicimport * defmain(): module = AnsibleModule(argument_spec={}) response = {"hello": "world"} module.exit_json(changed=False, meta=response) if __name__ == '__main__': main()
Notes main() 是你的程序入口 #!/usr/bin/python is 这个是必须的,就和些shell脚本一样 AnsibleModule 是从 ansible.module_utils.basic 总导入的,import * 必须是导入全部的 * AnsibleModule 有一些已经写好的方法让我们调用,例如exit_json play.ymlYAML
- hosts: localhost tasks: - name: Testthatmymoduleworks github_repo: register: result - debug: var=result
上边是一个非常简单的例子,调用了我们的module: github_repo, 并注册到一个变量result中,并打印这个变量的值
我们来跑一下:
$ ansible-playbookplay.yml(note: 命令中未指定 -i , 所以旧的版本可能需要你手动指定一个host文件-i).
输出:Bash
PLAY *************************************************************************** TASK [setup] ******************************************************************* ok: [localhost] TASK [Testthatmymoduleworks] *********************************************** ok: [localhost] TASK [debug] ******************************************************************* ok: [localhost] => { "result": { "changed": false, "meta": { "hello": "world" } } } PLAYRECAP ********************************************************************* localhost: ok=3changed=0unreachable=0failed=0非常漂亮,运行成功了
加入一些变量当然,一个好的模块一定会有输入的,因为我们要创建repo,所以我们直接拿来一些 代码复用 (其实就是创建一个repo需要的参数)
YAML
- name: Create a githubRepo github_repo: github_auth_key: "..." name: "Hello-World", description: "This is your first repository", private: yes has_issues: no has_wiki: no has_downloads: no state: present register: result
好,然后我们在代码中添加如下内容:
Python
defmain(): fields = { "github_auth_key": {"required": True, "type": "str"}, "name": {"required": True, "type": "str" }, "description": {"required": False, "type": "str"}, "private": {"default": False, "type": "bool" }, "has_issues": {"default": True, "type": "bool" }, "has_wiki": {"default": True, "type": "bool" }, "has_downloads": {"default": True, "type": "bool" }, "state": { "default": "present", "choices": ['present', 'absent'], "type": 'str' }, } module = AnsibleModule(argument_spec=fields) module.exit_json(changed=False, meta=module.params) 注意: 输入的变量是字典形式错在的 参数类型: str, bool, dict, list, … 我们可以指定一些default,required之类的.上边的例子,我们什么都没有做,只是把传递的值有传递到了exit_json
如果这时候跑一下的话,输出结果将会是我们传递的字典内容
好的,我们现在将一下如何处理这些传递进module的参数
在pythyon的头部我们添加两个函数(函数名称随意,但是要和下变的对应)
Python
defgithub_repo_present(data): has_changed = False meta = {"present": "not yet implemented"} return (has_changed, meta) defgithub_repo_absent(data=None): has_changed = False meta = {"absent": "not yet implemented"}
These are the functions that will actually do the work. Back in our main() method, let’s add some code to call the desired function:
这两个方法是真正用来处理数据的地方,让我们在main还数中添加如何调用这两个函数的部分
Python
defmain(): fields = {..} choice_map = { "present": github_repo_present, "absent": github_repo_absent, } module = AnsibleModule(argument_spec=fields) has_changed, result = choice_map.get(module.params['state'])(module.params) module.exit_json(changed=has_changed, meta=result)We use a map to map the state provided to a function that will handle that state.
我们通过map来控制这两个函数
最后的事情就是,让我们的函数真正具有创建和删除的功能(就是调用github的接口,原理还是通过url来访问接口)
Python
defgithub_repo_present(data): api_key = data['github_auth_key'] deldata['state'] deldata['github_auth_key'] headers = { "Authorization": "token {}" . format(api_key) } url = "{}{}" . format(api_url, '/user/repos') result = requests.post(url, json.dumps(data), headers=headers) if result.status_code == 201: return False, True, result.json() if result.status_code == 422: return False, False, result.json() # default: something went wrong meta = {"status": result.status_code, 'response': result.json()} return True, False, meta defgithub_repo_absent(data=None): headers = { "Authorization": "token {}" . format(data['github_auth_key']) } url = "{}/repos/{}/{}" . format(api_url, "toast38coza", data['name']) result = requests.delete(url, headers=headers) if result.status_code == 204: return False, True, {"status": "SUCCESS"} if result.status_code == 404: result = {"status": result.status_code, "data": result.json()} return False, False, result else: result = {"status": result.status_code, "data": result.json()} return True, False, result我们再添加一块来删除刚刚创建的repo:
YAML
- hosts: localhost vars: - github_token: "..." tasks: - name: Create a githubRepo github_repo: github_auth_key: "{{github_token}}" name: "Hello-World" description: "This is your first repository" private: yes has_issues: no has_wiki: no has_downloads: no - name: Deletethatrepo github_repo: github_auth_key: "{{github_token}}" name: "Hello-World" state: absent
最后一步,我们来添加一些主事,和测试的例子Python
DOCUMENTATION = ''' --- module: github_repo short_description: Manage your repos on Github ''' EXAMPLES = ''' - name: Create a github Repo github_repo: github_auth_key: "..." name: "Hello-World" description: "This is your first reposi