ansible playbook詳細教程(個人筆記)
內容比較雜多,根據需求ctrl F ^_^
執行playbook命令
ansibleplaybook -i "inventory文件名" playbook.yml f 10 (並行級別10)
加參數
-e "temp_file=${uuid}" 或者: --extravars "version=1.23.45 other_variable=foo" 或者: --extravars ‘{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}‘ 或者: --extravars "@some_file.json"
執行一個 playbook 之前,農想看看這個 playbook 的執行會影響到哪些 hosts,可以這樣做:
ansibleplaybook playbook.yml --list-hosts
實踐例子:https://github.com/ansible/ansibleexamples
playbook.yml 文件like 可 this:
---
- hosts: webservers,webservers2
vars:
http_port: 80
max_clients: 200remote_user: root
tasks:
- name: ensure apache is at the latest version(確定是不是最新版本)
yum: pkg=httpd state=latest
remote_user: yourname
- name: write the apache config file
template: src=/srv/httpd.j2 dest=/etc/httpd.conf
notify:
- restart apache
- name: ensure apache is runningservice: name=httpd state=started
handlers:
- name: restart apache
service: name=httpd state=restarted
inventory文件例子:
[all:vars]
DOCKERREGISTRY=kubernetes-master
DOCKERREGISTRYPROT=5000
IMAGESVERSION=0.0.4-SNAPSHOT
[k8s_emu_vip_node]
k8s_emu_vip_node1 ansible_ssh_host=10.222.2.201 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
[k8s_emu_node]
k8s_emu_node1 ansible_ssh_host=1.1.1.1 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
k8s_emu_node2 ansible_ssh_host=1.1.1.2 ansible_connection=ssh ansible_ssh_user=root ansible_ssh_pass=gsta123 ansible_ssh_extra_args="-o StrictHostKeyChecking=no"
{{}}裏進行操作,lookup插件,讀取network_test_conf文件,然後作為變量
{{ lookup(‘file‘, network_test_conf) | from_yaml }}
註冊node,添加主機,添加組
- name: Register ansible node list
add_host:
hostname: ‘{{item["name"]}}‘
groups: ‘fio-server‘
# ansible_ssh_host: ‘{{item.addresses[network_name] | selectattr("OS-EXT-IPS:type","equalto", "floating")| map(attribute="addr") | join(",")}}‘
ansible_ssh_host: ‘{{item.addresses[network_name] | selectattr("OS-EXT-IPS:type","equalto", "fixed")| map(attribute="addr") | join(",")}}‘
ansible_ssh_port: "22"
ansible_ssh_user: ‘{{image_ssh_user}}‘
ansible_ssh_pass: ‘{{image_ssh_pass}}‘
ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"
ansible_sftp_extra_args: "-o StrictHostKeyChecking=no"
#ansible_ssh_extra_args: "-o ProxyCommand=‘{{proxy}}‘ -o StrictHostKeyChecking=no"
#ansible_sftp_extra_args: "-o ProxyCommand=‘{{proxy}}‘ -o StrictHostKeyChecking=no" with_items: "{{result.json.servers}}"
copy模塊[拷貝文件,從本地拷貝文件至各主機]
- name: Copy the keyfile for authentication
copy: src=/wjf/weijunfeng dest={{ mongodb_datadir_prefix }}/secret owner=mongod group=mongod mode=0400
fetch模塊,從各主機拷貝文件到本地,flat: "yes"表示不自動創建目錄,如果為false,會自動創建帶各自主機名的目錄
fetch:
src: "/home/fio-result"
dest: "./fio-test-result/{{ansible_ssh_host}}/"
flat: "yes"
創建文件或者目錄,
- name: Create data directory for mongoc configuration server
file: path={{ mongodb_datadir_prefix }}/configdb state=directory owner=mongod group=mongod
l path: 路徑,一般用於創建刪除文件或目錄
l state: file的相關操作,
directory表示創建目錄,
link表示創建軟連接,link還需要源路徑和目標路徑配合使用
touch表示創建文件,
absent表示刪除文件
執行shell命令
- name: Initialize the replication set
shell: /usr/bin/mongo --port "{{ mongod_port }}" /tmp/repset_init.js
暫停,等待
- name: pause pause: seconds=20
service服務模塊 比如啟動nginx
- name: Make sure nginx start with boot
service: name=nginx state=started enabled=yes
enabled:這個註意了,默認是no,如果配置成false就是restart了之後就不關心結果了,配置成yes是要關心結果。
解壓模塊
- unarchive: src=foo.tgz dest=/var/lib/foo
- unarchive: src=/tmp/foo.zip dest=/usr/local/bin copy=no
- unarchive: src=https://example.com/example.zip dest=/usr/local/bin copy=no
unarchive模塊
用於解壓文件,模塊包含如下選項:
copy:在解壓文件之前,是否先將文件復制到遠程主機,默認為yes。若為no,則要求目標主機上壓縮包必須存在。
creates:指定一個文件名,當該文件存在時,則解壓指令不執行
dest:遠程主機上的一個路徑,即文件解壓的路徑
grop:解壓後的目錄或文件的屬組
list_files:如果為yes,則會列出壓縮包裏的文件,默認為no,2.0版本新增的選項
mode:解決後文件的權限
src:如果copy為yes,則需要指定壓縮文件的源路徑
owner:解壓後文件或目錄的屬主
yum模塊
- name: remove epel if installed
yum:
name: epel-release
state: absent
ignore_errors: true
Wait_for模塊:
等待事情發生,例如等待數據庫啟動、web容器啟動等。
- name: wait for dnsmasq port 53
wait_for:
port: 53
timeout: 10
port:等待某端口號必須啟動
path:等待某文件必須創建
host:默認是127.0.0.1,為了滿足等待其它遠程服務器的場景
timeout的單位是秒
state:默認是started,也就是等待啟動或創建,也可能存在等待刪除或停止等場景。對象是端口的時候start狀態會確保端口是打開的,stoped狀態會確認端口是關閉的;對象是文件的時候,present或者started會確認文件是存在的,而absent會確認文件是不存在的。
Git模塊
對於git版本服務的操作模塊
- name: ANSISTRANO | GIT | Update remote repository
git:
repo: "{{ ansistrano_git_repo }}"
dest: "{{ ansistrano_deploy_to }}/repo"
version: "{{ ansistrano_git_branch }}"
accept_hostkey: true
update: yes
force: yes
register: ansistrano_git_result_update
when: ansistrano_git_identity_key_path|trim == ‘‘ and ansistrano_git_identity_key_remote_path|trim == ‘‘
repo:git倉庫的地址
dest:倉庫中的相對目錄
version:哪個版本
accept_hostkey:如果ssh_opts包含” -o StrictHostKeyChecking=no”,此參數可以省略,如果配置成true或yes,需要添加hostkey
update:是否要更新新版本
force:配置成yes,本地倉庫將永遠被倉庫服務端覆蓋
get_url模塊
也就是download操作:
- name: codelivery | download | Download artifact
get_url:
url: "{{ codelivery_product_url }}"
dest: "{{ codelivery_releases_dir }}/{{ codelivery_product_url | basename }}"
force_basic_auth: "{{ codelivery_download_force_basic_auth | default(omit) }}"
headers: "{{ codelivery_download_headers | default(omit) }}"
url:http的地址
dest:下載文件到目的機的路徑
force_basic_auth:在發起請求前是否發出權限校驗信息
headers:報文頭信息
uri模塊
比get_url功能更強大的http請求模塊,可以發起get、post、put等各種請求方式,也可以處理返回值及內容
- name: codelivery | healthcheck | urlcheck status==200?
uri:
url: "http://{{ codelivery_urlcheck_addr }}:{{ codelivery_urlcheck_port }}{{ codelivery_urlcheck_url }}"
method: GET
headers:
Host: "{{ codelivery_urlcheck_host }}"
timeout: 10
status_code: 200
return_content: no
debug模塊 打印出變量
- debug: msg="heat_failed_reason={{reason.stdout}}"
when: result.stdout=="CREATE_FAILED"
常用魔數
ansible_distribution=Ubuntu
ansible_distribution_version=14.04
ansible_distribution_major_version:系統的大版本號
ansible_os_family:系統的操作系統(‘RedHat’,’Debian’,’FreeBSD’)
自定義局部變量並賦值
- name: Define nginx_user.
set_fact:
nginx_user: "{{ __nginx_user }}"
when: nginx_user is not defined
支持從 sudo 執行命令:
--- - hosts: webservers remote_user: yourname
sudo: yes
可以登陸後,sudo 到不同的用戶身份,而不是使用 root:
---
- hosts: webservers
remote_user: yourname
sudo: yes
sudo_user: postgres
如果需要在使用 sudo 時指定密碼,可在運行 ansible-playbook 命令時加上選項 --ask-sudo-pass (-K). 如果使用 sudo 時,playbook 疑似被掛起,可能是在 sudo prompt 處被卡住,這時可執行 Control-C 殺死卡住的任務,再重新運行一次.
service moudle
tasks:
- name: make sure apache is running
service: name=httpd state=running
command 和 shell ,它們不使用 key=value 格式的參數:[執行命令,linux命令]
tasks:
- name: disable selinux
command: /sbin/setenforce 0
command module 和 shell module 時,需要關心返回碼信息,如果有一條命令,它的成功執行的返回碼不是0, 意思就是執行命令不成功,你可以通過下面例子進行忽略[忽略錯誤,跳過報錯]:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
假設在 ‘vars’ 那裏定義了一個變量 ‘vhost’ ,可以這樣使用:{{}} [使用參數方法]
tasks:
- name: create a virtual host file for {{ vhost }}
template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }}
‘notify’ 下列出的即是 handlers,比如當一個文件的內容被改動時,重啟兩個 services:[文件改變時執行]
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache
Handlers 也是一些 task 的列表,通過名字來引用,它們和一般的 task 並沒有什麽區別.Handlers 是由通知者進行 notify, 如果沒有被 notify,handlers 不會執行.不管有多少個通知者進行了 notify,等到 play 中的所有 task 執行完成之後,handlers 也只會被執行一次,handlers 例子:
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
- include: handlers/handlers.yml
include指令
一個 task include file foo.yml 由一個普通的 task 列表所組成,像這樣:
---
# possibly saved as tasks/foo.yml
- name: placeholder foo
command: /bin/foo
- name: placeholder bar
command: /bin/bar
Include 指令可以跟普通的 task 混合,所以呢,你可以這樣使用(後面可以加參數):[include參數]
tasks: - include: tasks/foo.yml wp_user=timmy
如果Ansible 1.4 及以後的版本,include 語法可更為精簡
tasks: - { include: wordpress.yml, wp_user: timmy, ssh_keys: [ ‘keys/one.txt‘, ‘keys/two.txt‘ ] }
從 1.0 版開始,Ansible 支持另一種傳遞變量到 include files 的語法,這種語法支持結構化的變量:
tasks: - include: wordpress.yml vars: wp_user: timmy some_list_variable: - alpha - beta - gamma
Roles
Roles 基於一個已知的文件結構,去自動的加載某些 vars_files,tasks 以及 handlers。基於 roles 對內容進行分組
roles目錄結構如下:
site.yml
webservers.yml
fooservers.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
webservers/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
哥哥姐姐們可以這樣使用roles
---
- hosts: webservers
roles:
- common
- webservers
這個 playbook 為一個角色 ‘x’ 指定了如下的行為:
- l 如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 將被添加到 play 中
- l 如果 roles/x/handlers/main.yml 存在, 其中列出的 handlers 將被添加到 play 中
- l 如果 roles/x/vars/main.yml 存在, 其中列出的 variables 將被添加到 play 中
- l 如果 roles/x/meta/main.yml 存在, 其中列出的 “角色依賴” 將被添加到 roles 列表中 (1.3 and later)
- l 所有 copy tasks 可以引用 roles/x/files/ 中的文件,不需要指明文件的路徑。
- l 所有 script tasks 可以引用 roles/x/files/ 中的腳本,不需要指明文件的路徑。
- l 所有 template tasks 可以引用 roles/x/templates/ 中的文件,不需要指明文件的路徑。
- l 所有 include tasks 可以引用 roles/x/tasks/ 中的文件,不需要指明文件的路徑。
在 Ansible 1.4 及之後版本,你可以為”角色”的搜索設定 roles_path 配置項。使用這個配置項將所有的 common 角色 check out 到一個位置,以便在多個 playbook 項目中可方便的共享使用它們
roles_path = /opt/mysite/roles
如果 roles 目錄下有文件不存在,這些文件將被忽略。比如 roles 目錄下面缺少了 ‘vars/’ 目錄,這也沒關系。
roles帶參數
---
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: ‘/opt/a‘, port: 5000 }
- { role: foo_app_instance, dir: ‘/opt/b‘, port: 5001 }
roles帶條件,when
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == ‘RedHat‘" }
roles分配tags
---
- hosts: webservers
roles:
- { role: foo, tags: ["bar", "baz"] }
如果 play 仍然包含有 ‘tasks’ section,這些 tasks 將在所有 roles 應用完成之後才被執行。
如果你希望定義一些 tasks,讓它們在 roles 之前以及之後執行,大佬你可以這樣做:
---
- hosts: webservers
pre_tasks:
- shell: echo ‘hello‘
roles:
- { role: some_role }
tasks:
- shell: echo ‘still busy‘
post_tasks:
- shell: echo ‘goodbye‘
角色默認變量
要創建默認變量,只需在 roles 目錄下添加 defaults/main.yml 文件。這些變量在所有可用變量中擁有最低優先級,可能被其他地方定義的變量(包括 inventory 中的變量)所覆蓋。
角色依賴
“角色依賴” 使你可以自動地將其他 roles 拉取到現在使用的 role 中。”角色依賴” 保存在 roles 目錄下的 meta/main.yml 文件中。這個文件應包含一列 roles 和 為之指定的參數,下面是在 roles/myapp/meta/main.yml 文件中的示例:
---
dependencies:
- { role: common, some_parameter: 3 }
- { role: apache, port: 80 }
- { role: postgres, dbname: blarg, other_parameter: 12 }
“角色依賴” 也可以通過源碼控制倉庫或者 tar 文件指定,使用逗號分隔:路徑、一個可選的版本(tag, commit, branch 等等)、一個可選友好角色名(嘗試從源碼倉庫名或者歸檔文件名中派生出角色名):
--- dependencies:
- { role: ‘git+http://git.example.com/repos/role-foo,v1.1,foo‘ }
- { role: ‘/path/to/tar/file.tgz,,friendly-name‘ }
“角色依賴” 總是在 role (包含”角色依賴”的role)之前執行,並且是遞歸地執行。默認情況下,作為 “角色依賴” 被添加的 role 只能被添加一次,如果另一個 role 將一個相同的角色列為 “角色依賴” 的對象,它不會被重復執行。但這種默認的行為可被修改,通過添加 allow_duplicates: yes 到meta/main.yml 文件中。 比如,一個 role 名為 ‘car’,它可以添加名為 ‘wheel’ 的 role 到它的 “角色依賴” 中:
---
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }
wheel 角色的 meta/main.yml 文件包含如下內容:
---
allow_duplicates: yes
dependencies:
- { role: tire }
- { role: brake }
最終的執行順序是這樣的:
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
YAML語法要求如果值以{{ foo }}開頭的話我們需要將整行用雙引號包起來.這是為了確認你不是想聲明一個YAML字典
這樣是不行的:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
你應該這麽做:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
Facts通過訪問遠程系統獲取相應的信息. 一個例子就是遠程主機的IP地址或者操作系統是什麽. 使用以下命令可以查看哪些信息是可用的:[查看主機信息]
如果你不需要使用你主機的任何fact數據,你已經知道了你系統的一切,那麽你可以關閉fact數據的獲取.這有利於增強Ansilbe面對大量系統的push模塊,或者你在實驗性平臺中使用Ansible.在任何playbook中可以這樣做:[關閉facts]
- hosts: whatever
gather_facts: no
獲取主機名 {{ ansible_nodename }}
ansible hostname -m setup
命令輸出如下:
"ansible_all_ipv4_addresses": ["REDACTED IP ADDRESS"],
"ansible_all_ipv6_addresses": ["REDACTED IPV6 ADDRESS"],
"ansible_architecture": "x86_64",
"ansible_bios_date": "09/20/2012",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-3.5.0-23-generic",
"quiet": true,
"ro": true,
"root": "UUID=4195bff4-e157-4e41-8701-e93f0aec9e22",
"splash": true
},
"ansible_date_time": {
"date": "2013-10-02",
"day": "02",
"epoch": "1380756810",
"hour": "19",
"iso8601": "2013-10-02T23:33:30Z",
"iso8601_micro": "2013-10-02T23:33:30.036070Z",
"minute": "33",
"month": "10",
"second": "30",
"time": "19:33:30",
"tz": "EDT",
"year": "2013"
},
"ansible_default_ipv4": {
"address": "REDACTED",
"alias": "eth0",
"gateway": "REDACTED",
"interface": "eth0",
"macaddress": "REDACTED",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "REDACTED",
"type": "ether"
},
"ansible_default_ipv6": {
},
"ansible_devices": {
"fd0": {
"holders": [],
"host": "",
"model": null,
"partitions": {
},
"removable": "1",
"rotational": "1",
"scheduler_mode": "deadline",
"sectors": "0",
"sectorsize": "512",
"size": "0.00 Bytes",
"support_discard": "0",
"vendor": null
},
"sda": {
"holders": [],
"host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)",
"model": "VMware Virtual S",
"partitions": {
"sda1": {
"sectors": "39843840",
"sectorsize": 512,
"size": "19.00 GB",
"start": "2048"
},
"sda2": {
"sectors": "2",
"sectorsize": 512,
"size": "1.00 KB",
"start": "39847934"
},
"sda5": {
"sectors": "2093056",
"sectorsize": 512,
"size": "1022.00 MB",
"start": "39847936"
}
},
"removable": "0",
"rotational": "1",
"scheduler_mode": "deadline",
"sectors": "41943040",
"sectorsize": "512",
"size": "20.00 GB",
"support_discard": "0",
"vendor": "VMware,"
},
"sr0": {
"holders": [],
"host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
"model": "VMware IDE CDR10",
"partitions": {
},
"removable": "1",
"rotational": "1",
"scheduler_mode": "deadline",
"sectors": "2097151",
"sectorsize": "512",
"size": "1024.00 MB",
"support_discard": "0",
"vendor": "NECVMWar"
}
},
"ansible_distribution": "Ubuntu",
"ansible_distribution_release": "precise",
"ansible_distribution_version": "12.04",
"ansible_domain": "",
"ansible_env": {
"COLORTERM": "gnome-terminal",
"DISPLAY": ":0",
"HOME": "/home/mdehaan",
"LANG": "C",
"LESSCLOSE": "/usr/bin/lesspipe %s %s",
"LESSOPEN": "| /usr/bin/lesspipe %s",
"LOGNAME": "root",
"LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
"MAIL": "/var/mail/root",
"OLDPWD": "/root/ansible/docsite",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PWD": "/root/ansible",
"SHELL": "/bin/bash",
"SHLVL": "1",
"SUDO_COMMAND": "/bin/bash",
"SUDO_GID": "1000",
"SUDO_UID": "1000",
"SUDO_USER": "mdehaan",
"TERM": "xterm",
"USER": "root",
"USERNAME": "root",
"XAUTHORITY": "/home/mdehaan/.Xauthority",
"_": "/usr/local/bin/ansible"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "REDACTED",
"netmask": "255.255.255.0",
"network": "REDACTED"
},
"ipv6": [{
"address": "REDACTED",
"prefix": "64",
"scope": "link"
}],
"macaddress": "REDACTED",
"module": "e1000",
"mtu": 1500,
"type": "ether"
},
"ansible_form_factor": "Other",
"ansible_fqdn": "ubuntu2.example.com",
"ansible_hostname": "ubuntu2",
"ansible_interfaces": ["lo",
"eth0"],
"ansible_kernel": "3.5.0-23-generic",
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [{
"address": "::1",
"prefix": "128",
"scope": "host"
}],
"mtu": 16436,
"type": "loopback"
},
"ansible_lsb": {
"codename": "precise",
"description": "Ubuntu 12.04.2 LTS",
"id": "Ubuntu",
"major_release": "12",
"release": "12.04"
},
"ansible_machine": "x86_64",
"ansible_memfree_mb": 74,
"ansible_memtotal_mb": 991,
"ansible_mounts": [{
"device": "/dev/sda1",
"fstype": "ext4",
"mount": "/",
"options": "rw,errors=remount-ro",
"size_available": 15032406016,
"size_total": 20079898624
}],
"ansible_nodename": "ubuntu2.example.com",
"ansible_os_family": "Debian",
"ansible_pkg_mgr": "apt",
"ansible_processor": ["Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz"],
"ansible_processor_cores": 1,
"ansible_processor_count": 1,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 1,
"ansible_product_name": "VMware Virtual Platform",
"ansible_product_serial": "REDACTED",
"ansible_product_uuid": "REDACTED",
"ansible_product_version": "None",
"ansible_python_version": "2.7.3",
"ansible_selinux": false,
"ansible_ssh_host_key_dsa_public": "REDACTED KEY VALUE""ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE""ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE""ansible_swapfree_mb": 665,
"ansible_swaptotal_mb": 1021,
"ansible_system": "Linux",
"ansible_system_vendor": "VMware, Inc.",
"ansible_user_id": "root",
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "VMware"
本地Facts
如果遠程受管理的機器有一個 “/etc/ansible/facts.d” 目錄,那麽在該目錄中任何以 ”.fact”結尾的文件都可以在Ansible中提供局部facts.這些文件可以是JSON,INI或者任何可以返回JSON的可執行文件.
例如建設有一個 /etc/ansible/facts.d/perferences.fact文件:
[general]
asdf=1
bar=2
這將產生一個名為 “general” 的哈希表fact,裏面成員有 ‘asdf’ 和 ‘bar’. 可以這樣驗證:
ansible <hostname> -m setup -a "filter=ansible_local"
然後你會看到有以下fact被添加:
"ansible_local": {
"preferences": {
"general": {
"asdf" : "1",
"bar" : "2" } } }
而且也可以在template或palybook中訪問該數據:
{{ ansible_local.preferences.general.asdf }}
本地命名空間放置其它用戶提供的fact或者playbook中定義的變量覆蓋系統facts值.
如果你有個一個playook,它復制了一個自定義的fact,然後運行它,請顯式調用來重新運行setup模塊,這樣可以讓我們在該playbook中使用這些fact.否則,在下一個play中才能獲取這些自定義的fact信息.這裏有一個示例:
- hosts: webservers
tasks:
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom impi fact
copy: src=ipmi.fact dest=/etc/ansible/facts.d
- name: re-read facts after adding custom fact
setup: filter=ansible_local
然而在該模式中你也可以編寫一個fact模塊,這只不過是多了一個選項.
Fact緩存
從一個服務器引用另一個服務器的變量是可行的
{{ hostvars[‘asdf.example.com‘][‘ansible_os_family‘] }}
註冊變量
register
- hosts: web_servers
tasks:
- shell: /usr/bin/foo
register: foo_result
ignore_errors: True
- shell: /usr/bin/bar
when: foo_result.rc == 5
魔法變量,以及如何訪問其它主機的信息
Ansible會自動提供給你一些變量,即使你並沒有定義過它們.這些變量中重要的有 ‘hostvars’,’group_names’,和 ‘groups’.由於這些變量名是預留的,所以用戶不應當覆蓋它們. ‘environmen’ 也是預留的. hostvars可以讓你訪問其它主機的變量,包括哪些主機中獲取到的facts.如果你還沒有在當前playbook或者一組playbook的任何play中訪問那個主機,那麽你可以獲取變量,但無法看到facts值. 如果數據庫服務器想使用另一個節點的某個 ‘fact’ 值,或者賦值給該節點的一個inventory變量.可以在一個模板中甚至命令行中輕松實現:
{{ hostvars[‘test.example.com‘][‘ansible_distribution‘] }}
另外, group_names 是當前主機所在所有群組的列表(數組).所以可以使用Jinja2語法在模板中根據該主機所在群組關系(或角色)來產生變化:
{% if ‘webserver‘ in group_names %}
# some part of a configuration file that only applies to webservers
{% endif %}
groups 是inventory中所有群組(主機)的列表.可用於枚舉群組中的所有主機.例如:
{% for host in groups[‘app_servers‘] %}
# something that applies to all app servers.
{% endfor %}
一個經常使用的範式是找出該群組中的所有IP地址:
{% for host in groups[‘app_servers‘] %}
{{ hostvars[host][‘ansible_eth0‘][‘ipv4‘][‘address‘] }}
{% endfor %}
外部的變量文件:
---
- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
這可以保證你共享playbook源碼時隔離敏感數據的風險.
每個變量文件的內容是一個簡單的YAML文件,如下所示:
---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic
變量的優先級,變量優先級
* extra vars (-e in the command line) always win
* then comes connection variables defined in inventory (ansible_ssh_user, etc)
* then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
* then comes the rest of the variables defined in inventory
* then comes facts discovered about a system * then "role defaults", which are the most "defaulty" and lose in priority to everything.
* extra vars (在命令行中使用 -e)優先級最高
* 然後是在inventory中定義的連接變量(比如ansible_ssh_user)
* 接著是大多數的其它變量(命令行轉換,play中的變量,included的變量,role中的變量等)
* 然後是在inventory定義的其它變量
* 然後是由系統發現的facts
* 然後是 "role默認變量", 這個是最默認的值,很容易喪失優先權
when
- include: tasks/sometasks.yml
when: "‘reticulating splines‘ in output"
或者應用於role:
- hosts: webservers
roles:
- { role: debian_stock_config, when: ansible_os_family == ‘Debian‘ }
基於變量選擇文件和模版
循環
- name: add several users
user: name={{ item }} state=present groups=wheel
with_items:
- testuser1
- testuser2
或者
with_items: "{{somelist}}"
或者
- name: add several users
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: ‘testuser1‘, groups: ‘wheel‘ }
- { name: ‘testuser2‘, groups: ‘root‘ }
嵌套循環
- name: give users access to multiple databases
mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
with_nested:
- [ ‘alice‘, ‘bob‘ ]
- [ ‘clientdb‘, ‘employeedb‘, ‘providerdb‘ ]
對哈希表使用循環
New in version 1.5.
假如你有以下變量:
--- users:
alice:
name: Alice Appleworth
telephone: 123-456-7890
bob:
name: Bob Bananarama
telephone: 987-654-3210
你想打印出每個用戶的名稱和電話號碼.你可以使用 with_dict 來循環哈希表中的元素:
tasks:
- name: Print phone records
debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
with_dict: "{{users}}"
對文件列表使用循環
with_fileglob 可以以非遞歸的方式來模式匹配單個目錄中的文件.如下面所示:
---
- hosts: all
tasks:
# first ensure our target directory exists
- file: dest=/etc/fooapp state=directory
# copy each file over that matches the given pattern
- copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
with_fileglob:
- /playbooks/files/fooapp/*
對子元素使用循環
假設你想對一組用戶做一些動作,比如創建這些用戶,並且允許它們使用一組SSH key來登錄.
如何實現那? 先假設你有按以下方式定義的數據,可以通過”vars_files”或”group_vars/all”文件加載:
--- users:
- name: alice
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
mysql:
password: mysql-password
hosts:
- "%"
- "127.0.0.1"
- "::1"
- "localhost"
privs:
- "*.*:SELECT"
- "DB1.*:ALL"
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
mysql:
password: other-mysql-password
hosts:
- "db1"
privs:
- "*.*:SELECT"
- "DB2.*:ALL"
那麽可以這樣實現:
- user: name={{ item.name }} state=present generate_ssh_key=yes
with_items: "{{users}}"
- authorized_key: "user={{ item.0.name }} key=‘{{ lookup(‘file‘, item.1) }}‘"
with_subelements:
- users
- authorized
根據mysql hosts以及預先給定的privs subkey列表,我們也可以在嵌套的subkey中叠代列表:
- name: Setup MySQL users
mysql_user: name={{ item.0.user }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join(‘/‘) }}
with_subelements:
- users
- mysql.hosts Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given key inside of those records.
你也可以為字元素列表添加第三個元素,該元素可以放置標誌位字典.現在你可以加入’skip_missing’標誌位.如果設置為True,那麽查找插件會跳過不包含指定子鍵的列表條目.如果沒有該標誌位,或者標誌位值為False,插件會產生錯誤並指出缺少該子鍵.
這就是authorized_key模式中key的獲取方式.
對整數序列使用循環
with_sequence 可以以升序數字順序生成一組序列.你可以指定起始值、終止值,以及一個可選的步長值.
指定參數時也可以使用key=value這種鍵值對的方式.如果采用這種方式,’format’是一個可打印的字符串.
數字值可以被指定為10進制,16進制(0x3f8)或者八進制(0600).負數則不受支持.請看以下示例:
---
- hosts: all
tasks: # create groups
- group: name=evens state=present
- group: name=odds state=present
# create some test users
- user: name={{ item }} state=present groups=evens
with_sequence: start=0 end=32 format=testuser%02x
# create a series of directories with even numbers for some reason
-file: dest=/var/stuff/{{ item }} state=directory
with_sequence: start=4 end=16 stride=2
# a simpler way to use the sequence plugin
# create 4 groups
- group: name=group{{ item }} state=present
with_sequence: count=4
隨機選擇
‘random_choice’功能可以用來隨機獲取一些值.它並不是負載均衡器(已經有相關的模塊了).它有時可以用作一個簡化版的負載均衡器,比如作為條件判斷:
- debug: msg={{ item }}
with_random_choice:
- "go through the door"
- "drink from the goblet"
- "press the red button"
- "do nothing"
Do-Until循環
有時你想重試一個任務直到達到某個條件.比如下面這個例子:[重復執行]
- action: shell /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
上面的例子遞歸運行shell模塊,直到模塊結果中的stdout輸出中包含”all systems go”字符串,或者該任務按照10秒的延遲重試超過5次.”retries”和”delay”的默認值分別是3和5.
該任務返回最後一個任務返回的結果.單次重試的結果可以使用-vv選項來查看. 被註冊的變量會有一個新的屬性’attempts’,值為該任務重試的次數.
查找第一個匹配的文件
這其實不是一個循環,但和循環很相似.如果你想引用一個文件,而該文件是從一組文件中根據給定條件匹配出來的.這組文件中部分文件名由變量拼接而成.針對該場景你可以這樣做:[動態文件名]
- name: INTERFACES | Create Ansible header for /etc/network/interfaces
template: src={{ item }} dest=/etc/foo.conf
with_first_found:
- "{{ansible_virtualization_type}}_foo.conf"
- "default_foo.conf"
該功能還有一個更完整的版本,可以配置搜索路徑.請看以下示例:
- name: some configuration template
template: src={{ item }} dest=/etc/file.cfg mode=0444 owner=root group=root
with_first_found:
- files:
- "{{inventory_hostname}}/etc/file.cfg"
paths:
- ../../../templates.overwrites
- ../../../templates
- files:
- etc/file.cfg
paths:
- templates
異步操作和輪詢
默認情況下playbook中的任務執行時會一直保持連接,直到該任務在每個節點都執行完畢.有時這是不必要的,比如有些操作運行時間比SSH超時時間還要長.
解決該問題最簡單的方式是一起執行它們,然後輪詢直到任務執行完畢,簡單的意思就是,像下面的例子,執行任務後,ansible就不等它了,往下執行下一個任務,然後每隔5秒鐘去看看它執行完成沒,超時時間為45秒,async參數值代表了這個任務執行時間的上限值。即任務執行所用時間如果超出這個時間,則認為任務失敗。此參數若未設置,則為同步執行。
你也可以對執行時間非常長(有可能遭遇超時)的操作使用異步模式.
為了異步啟動一個任務,可以指定其最大超時時間以及輪詢其狀態的頻率.如果你沒有為 poll 指定值,那麽默認的輪詢頻率是10秒鐘:
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
command: /bin/sleep 15
async: 45
poll: 5
async 並沒有默認值,如果你沒有指定 async 關鍵字,那麽任務會以同步的方式運行,這是Ansible的默認行為.
另外,如果你不需要等待任務執行完畢,你可以指定 poll 值為0而啟用 “啟動並忽略”
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op, allow to run for 45 sec, fire and forget
command: /bin/sleep 15
async: 45
poll: 0
改為”啟動並忽略,稍後再檢查”,你可以使用以下方式執行任務:
---
# Requires ansible 1.8+
- name: ‘YUM - fire and forget task‘
yum: name=docker-io state=installed
async: 1000
poll: 0
register: yum_sleeper
- name: ‘YUM - check on fire and forget task‘
async_status: jid={{ yum_sleeper.ansible_job_id }}
register: job_result
until: job_result.finished
retries: 30
如果 async: 值太小,可能會導致 “稍後檢查” 任務執行失敗,因為 async_status:: 的臨時狀態文件還未被寫入信息,而”稍後檢查”任務就試圖讀取此文件.
delegate_to 選中主機執行
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
本地執行
---
# ...
tasks:
- name: recursively copy files from management server to target
local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Run Once
New in version 1.7.
有時候你有這樣的需求,在一個主機上面只執行一次一個任務.這樣的配置可以配置”run_once”來實現:
---
# ...
tasks:
# ...
- command: /opt/application/upgrade_db.py
run_once: true # ...
這樣可以添加在”delegat_to”選項對中來定義要執行的主機:
- command: /opt/application/upgrade_db.py
run_once: true
delegate_to: web01.example.org
當”run_once” 沒有喝”delegate_to”一起使用,這個任務將會被清單指定的第一個主機. 在一組被play制定主機.例如 webservers[0], 如果play指定為 “hosts: webservers”.
這個方法也很類似,雖然比使用條件更加簡單粗暴,如下事例:
- command: /opt/application/upgrade_db.py
when: inventory_hostname == webservers[0]
本地Playbooks
在本地使用playbook有時候比ssh遠程使用更加有用.可以通過把playbook放在crontab中,來確保一個系統的配置,可以很有用. 在OS installer 中運行一個playbook也很有用.例如Anaconda kickstart.
要想在本地運行一個play,可以直接設置”host:” 與 “hosts:127.0.0.1”, 然後使用下面的命令運行:
ansible-playbook playbook.yml --connection=local
或者,一個本地連接也可以作為一個單獨的playbook play應用在playbook中, 即便playbook中其他的plays使用默認遠程 連接如下:
- hosts: 127.0.0.1
connection: local
environment 使用代理上網
- hosts: all
remote_user: root
tasks:
- apt: name=cobbler state=installed
environment:
http_proxy: http://proxy.example.com:8080
environment 也可以被存儲在變量中,像如下方式訪問:
- hosts: all
remote_user: root
# here we make a variable named "proxy_env" that is a dictionary
vars:
proxy_env:
http_proxy: http://proxy.example.com:8080
tasks:
- apt: name=cobbler state=installed
environment: proxy_env
指定錯誤條件
[判定錯誤]
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "‘FAILED‘ in command_result.stderr"
在 Ansible 1.4 之前的版本能通過如下方式完成:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result ignore_errors: True
- name: fail the play if the previous command did not succeed
fail: msg="the command failed"
when: "‘FAILED‘ in command_result.stderr
不輸出結果,不報告狀態,覆寫結果
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
# this will never report ‘changed‘ status
- shell: wall ‘beep‘
changed_when: False
標簽tags
如果你有一個大型的 playbook,那能夠只運行其中特定部分的配置而無需運行整個 playbook 將會很有用.
plays 和 tasks 都因這個理由而支持 “tags:”
例:
tasks:
- yum: name={{ item }} state=installed
with_items:
- httpd
- memcached
tags:
- packages
- template: src=templates/src.j2 dest=/etc/foo.conf
tags:
- configuration
如果你只想運行一個非常大的 playbook 中的 “configuration” 和 “packages”,你可以這樣做:
ansible-playbook example.yml --tags "configuration,packages"
另一方面,如果你只想執行 playbook 中某個特定任務 之外 的所有任務,你可以這樣做:
ansible-playbook example.yml --skip-tags "notification"
你同樣也可以對 roles 應用 tags:
roles:
- { role: webserver, port: 5000, tags: [ ‘web‘, ‘foo‘ ] }
你同樣也可以對基本的 include 語句使用 tag:
- include: foo.yml tags=web,foo
從指定任務開始運行palybook以及分步運行playbook
以下列出了幾種方式來運行playbook.這對於測試或調試新的playbook很有幫助.
Start-at-task
如果你想從指定的任務開始執行playbook,可以使用``–start-at``選項:
ansible-playbook playbook.yml --start-at="install packages"
以上命令就會在名為”install packages”的任務開始執行你的playbook.
分步運行playbook
我們也可以通過``–step``選項來交互式的執行playbook:
ansible-playbook playbook.yml --step
這樣ansible在每個任務前會自動停止,並詢問是否應該執行該任務.
比如你有個名為``configure ssh``的任務,playbook執行到這裏會停止並詢問:
Perform task: configure ssh (y/n/c):
“y”回答會執行該任務,”n”回答會跳過該任務,而”c”回答則會繼續執行剩余的所有任務而不再詢問你.
python ansible api調用
#!/usr/bin/env python2 import sys import json import shutil import pprint from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars.manager import VariableManager from ansible.inventory.manager import InventoryManager from ansible.playbook.play import Play from ansible.executor.task_queue_manager import TaskQueueManager from ansible.plugins.callback import CallbackBase import ansible.constants as C from ansible.inventory.group import Group from ansible.inventory.host import Host def get_info(username,password,resource): class ResultCallback(CallbackBase): def __init__(self, *args, **kwargs): self.info = {} def v2_runner_on_ok(self, result, **kwargs): host = result._host self.info[host.name] = result._result Options = namedtuple(‘Options‘, [‘connection‘, ‘module_path‘, ‘forks‘, ‘become‘, ‘become_method‘, ‘become_user‘, ‘check‘, ‘diff‘]) options = Options(connection=‘local‘, module_path=[‘/to/mymodules‘], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False) loader = DataLoader() results_callback = ResultCallback() #inventory = InventoryManager(loader=loader, sources=[tempFileName]) inventory = InventoryManager(loader=loader) inventory.add_group("default") for host in resource: inventory.add_host(host=host,port=22,group=‘default‘) variable_manager = VariableManager(loader=loader, inventory=inventory) for host in resource: host = inventory.get_host(hostname=host) variable_manager.set_host_variable(host=host,varname=‘ansible_ssh_host‘,value=host) variable_manager.set_host_variable(host=host,varname=‘ansible_ssh_user‘,value=username) variable_manager.set_host_variable(host=host,varname=‘ansible_ssh_pass‘,value=password) variable_manager.set_host_variable(host=host,varname=‘ansible_connection‘,value=‘local‘) play_source = dict( name = "Ansible Play", hosts = resource, gather_facts = ‘no‘, tasks = [ dict(action=dict(module=‘setup‘, args=‘‘)) ] ) play = Play().load(play_source, variable_manager=variable_manager, loader=loader) tqm = None try: tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=None, stdout_callback=results_callback, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() shutil.rmtree(C.DEFAULT_LOCAL_TMP, True) return results_callback.info def handle_info(rawdata, node): # data dictionary data = {} detail = {} # read sysinfo cpu_count = rawdata[node][‘ansible_facts‘][‘ansible_processor_count‘] cpu_cores = rawdata[node][‘ansible_facts‘][‘ansible_processor_cores‘] cpu_model = list(set(rawdata[node][‘ansible_facts‘][‘ansible_processor‘][2::3])) cpu_vcpus = rawdata[node][‘ansible_facts‘][‘ansible_processor_vcpus‘] # memtotal = rawdata[node][‘ansible_facts‘][‘ansible_memtotal_mb‘] # memfree = rawdata[node][‘ansible_facts‘][‘ansible_memfree_mb‘] memory = rawdata[node][‘ansible_facts‘][‘ansible_memory_mb‘][‘real‘] disk_info = rawdata[node][‘ansible_facts‘][‘ansible_devices‘] interfaces_info = rawdata[node][‘ansible_facts‘][‘ansible_interfaces‘] for i in range(len(interfaces_info)): tmp = "ansible_" + interfaces_info[i].replace("-", "_") if rawdata[node][‘ansible_facts‘][tmp].has_key(‘type‘): if rawdata[node][‘ansible_facts‘][tmp][‘type‘] == "bridge": continue detail[interfaces_info[i]] = {} detail[interfaces_info[i]][‘active‘] = rawdata[node][‘ansible_facts‘][tmp][‘active‘] detail[interfaces_info[i]][‘mtu‘] = rawdata[node][‘ansible_facts‘][tmp][‘mtu‘] detail[interfaces_info[i]][‘promisc‘] = rawdata[node][‘ansible_facts‘][tmp][‘promisc‘] if rawdata[node][‘ansible_facts‘][tmp].has_key(‘speed‘): detail[interfaces_info[i]][‘speed‘] = rawdata[node][‘ansible_facts‘][tmp][‘speed‘] if rawdata[node][‘ansible_facts‘][tmp].has_key(‘type‘): detail[interfaces_info[i]][‘type‘] = rawdata[node][‘ansible_facts‘][tmp][‘type‘] # store sysinfo into data dictionary cpu = {} cpu[‘cpu_number‘] = cpu_count cpu[‘cpu_cores‘] = cpu_cores cpu[‘cpu_model‘] = cpu_model cpu[‘cpu_vcpus‘] = cpu_vcpus data[‘cpu‘] = cpu data[‘memory‘] = memory disk = {} disk[‘number‘] = len(disk_info) disk[‘info‘] = {} for key in disk_info: disk[‘info‘][key] = {} disk[‘info‘][key][‘size‘] = disk_info[key][‘size‘] data[‘disk‘] = disk interfaces = {} interfaces[‘number‘] = len(interfaces_info) interfaces[‘info‘] = detail data[‘interfaces‘] = interfaces return data def handle_infos(rawdata): info = [] for key in rawdata: temp = {} temp[‘IP_ADDR‘] = key temp[‘OBJ_ATTRS‘] = handle_info(rawdata, key) info.append(temp) return info if __name__ == ‘__main__‘: if len(sys.argv) != 4: print "need parameter:username,password,list of hosts(split with ‘,‘)!!!" sys.exit() hosts=sys.argv[3].split(",") raw_data = get_info(sys.argv[1],sys.argv[2],hosts) data = handle_infos(raw_data) data = json.dumps(data) pprint.pprint(data)
晚安( ̄o ̄) . z Z
ansible playbook詳細教程(個人筆記)