1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ansibleのlineinfileモジュールでパターンの否定と後方参照する

Last updated at Posted at 2018-03-27

目的としては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

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?