目的としてはCentOS7で変なのになるNICのデバイス名をeth0とかの従来のままにしたかっただけで、
そのタスクを書こうとした備忘録です。
オンプレで物理ならOS入れる時にvmlinuzの後ろにnet.ifnames=0
オプションつければいけるみたい。
あとはcobblerとかでもいいみたいですね。
要はgrubの設定ファイルの任意の1行を特定の文字列がない場合に部分的に文字列置換をしたかったんですけど、
ダブルクォートの後ろに文字列が追加されたり2重に追加されたりしないようにする正規表現的なところに手間取りました。
で、、以下でまあなんとかなりそうな気がするのでtemplateにせずにlineinfileでいけそう。
(templateにするとバージョンとか環境で色々違う場合に一様になってしまうのに抵抗感がある設定ファイルだとちょっとめんどくさいことになるというか)
まあOSイメージに入ってたら要らないんですけどね。(packer側のタスクにいれといたほうが効率よさそうなやつ)
以下では適当なファイルを作成して試していますが本来設定したいファイルは/etc/default/grub
です。
# ansible localhost -c local -m lineinfile -b \
-a 'dest=./hoge regexp="(^GRUB_CMDLINE_LIN*(?!.*net.ifnames=0).*)(\")$" line="\1 net.ifnames=0 biosdevname=0\"" backrefs=yes' -C -D
--- before: ./hoge (content)
+++ after: ./hoge (content)
@@ -1,5 +1,5 @@
GRUB_CMDLINE_LINUX="console=tty1 console=ttyS0,115200n8 earlyprintk=ttyS0,115200 rootdelay=300 net.ifnames=0"
-GRUB_CMDLINE_LINUX="rhgb quiet"
+GRUB_CMDLINE_LINUX="rhgb quiet net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="rhgb quiet net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="rd.lvm.lv=centos/swap crashkernel=auto rhgb quiet net.ifnames=0 biosdevname=0"
localhost | SUCCESS => {
"backup": "",
"changed": true,
"msg": "line replaced"
}
パターンの否定と後方参照のあたりがだいぶ勉強になりました。
正規表現:文字列を「含まない」否定の表現まとめ | WWWクリエイターズ
Ansibleのlineinfileのオプションを詳しく見てみる – Koltatt
ansibleのlineinfileモジュールはbackrefsをyesにすると部分的にパターンマッチしたやつを置換後の文字列として使えるようです。
正規表現のパターンマッチの時にカッコ()でくくって一致した文字列を置換後に\1と書いて呼ぶのが後方参照。
1番目のカッコは\1、2番目のカッコは\2と書いて呼ぶかんじです。
以下のAnsibleオプションを解説すると、net.ifnames=0
に一致しないがGRUB_CMDLINE_LIN
から始まるダブルクォートの手前までを\1で後で見られるようにカッコ()でくくっておいて、
置換後の文字列で置換前の文字列のうしろに必要な設定を追加してダブルクォートで閉じる、ということです。
'dest=./hoge regexp="(^GRUB_CMDLINE_LIN*(?!.*net.ifnames=0).*)(\")$" line="\1 net.ifnames=0 biosdevname=0\"" backrefs=yes'
パターンの否定の部分、一致しないにあたるのは、(?!.*PATTERN)
ですが、詳しくは参考リンク先をどうぞ。
これでとりあえず見たことあるパターンでおかしな動作はしないことはたぶん確認できた気がします。
taskとしてはこんな感じに。
- name: delete config of udev's network device naming rules.
file:
dest: "/etc/udev/rules.d/{{ udevnwnamecfg }}"
state: absent
with_items:
- 60-net.rules
- 71-biosdevname.rules
- 75-net-description.rules
- 80-net-name-slot.rules
loop_control:
loop_var: udevnwnamecfg
- name: to hold nic-name at grub config of centos7 like a centos6
lineinfile:
backrefs: yes
dest: "/etc/default/grub"
regexp: "(^GRUB_CMDLINE_LIN*(?!.*net.ifnames=0).*)(\")$"
line: '\1 net.ifnames=0 biosdevname=0\"'
で、これがしてあってOS再起動が行われていさえすればインタフェースファイル名はむかしのとおりと思うのですが、すでに違う名前で上がってきてるやつもどうにかeth0とかにしたいという場合のためだけに作ったタスクが以下。
(NICを手動で固定にしたいケースのみでdhcpやNetworkmanagerのmethodがautoだと向いてないと思われる。)
# デバイス名にeth0が含まれなかったらloopでlo以外の全てのデバイスを変更
- name: check network device names. #戻り値はens192\nens224\nのようになる
shell: ls -1 /etc/sysconfig/network-scripts/ifcfg-*|grep -v lo|sed -e 's,/etc/sysconfig/network-scripts/ifcfg-,,g'
register: chk_nwif_name
check_mode: no
ignore_errors: yes
changed_when: false
- block:
- name: copy network device file
copy:
remote_src: true
src: "/etc/sysconfig/network-scripts/ifcfg-{{ nwdev[1] }}" #ens192など添え字1のほうが順に入る
dest: "/etc/sysconfig/network-scripts/ifcfg-eth{{ nwdev[0] }}" #0など添え字0側が順に入る
register: cp_nwif_res
with_indexed_items: "{{ chk_nwif_name.stdout_lines }}" #indexつきでloopで改行区切りで読まれる[0,ens192\n1,ens224]
loop_control:
loop_var: nwdev
- name: set fact interfacefiles
set_fact:
ifcfg_dests: "{{ cp_nwif_res.results | map(attribute='dest') | list }}" #[/etc/sysconfig/network-scripts/ifcfg-ens192,/etc/...]という戻り値
ifcfg_srcs: "{{ cp_nwif_res.results | map(attribute='src') | list }}" #[/etc/sysconfig/network-scripts/ifcfg-eth0,/etc/...]という戻り値
- name: replace old network name line on new name network device config.
lineinfile:
dest: "{{ repif[0] }}"
regexp: "^{{ repif[2] }}={{ repif[1] | basename | regex_replace('ifcfg-') }}" #^NAME=ens192などになる
line: "{{ repif[2] }}={{ repif[0] | basename | regex_replace('ifcfg-') }}" #NAME=eth0などになる
with_nested:
- "{{ ifcfg_dests }}"
- "{{ ifcfg_srcs }}"
- [ 'NAME', 'DEVICE' ] #with_nestedによりすべての組み合わせで作動する
loop_control:
loop_var: repif
- name: delete old network device name file
file:
path: "{{ delif }}" #[/etc/sysconfig/network-scripts/ifcfg-ens192,/etc/...]が入る
state: absent
with_items: "{{ ifcfg_srcs }}"
loop_control:
loop_var: delif
notify:
- OS-reboot
- OS-start-wait
when: not 'eth0' in chk_nwif_name.stdout_lines #eth0がインタフェースファイルの中に無かったらblockの中身が作動
これ書くのちょっと自分の脳味噌的にアレで時間かかりました。
(nestedだとdestとsrcも連結して呼んでからloop内で分離して使うみたいな風に変えないとアレかもしれない)
まあでもループ処理とその変数の扱いについての理解が進んで楽しかったりはしました。
ただこれAMIとかOSテンプレートとかOSイメージ側に事前にgrubなどの設定がしてある場合は不要だと思われ。
要るケースは事前のイメージの設定が漏れててすでにやらかしてる時だけかな。
loopでcpした元ファイルを間違いのないように消したい実はmvしたかった的な用途には上記が多少役立つかも。
ただcopyモジュールに元ファイル削除オプションついてたらよかったのにとちょっと思いました。
参考:
正規表現:文字列を「含まない」否定の表現まとめ | WWWクリエイターズ
Ansibleのlineinfileのオプションを詳しく見てみる – Koltatt
【CentOS 7】ネットワークインタフェース名をeth0などの旧来の方式に戻す方法 | Step On Board
CentOS7におけるNIC命名ルール - 雑木林
lineinfile - Ensure a particular line is in a file, or replace an existing line using a back-referenced regular expression. — Ansible Documentation