一見これで良さそうだが

Ansibleで対象ホストのIPアドレスを変更して、さらに処理が継続されるようなplaybookを考えてみる。
Ansible v2で操作対象がCentOS 7だと以下のような感じになる。

- hosts: all
  tasks:
  - (前処理)
  - command: nmcli connection modify {{ nic }} ipv4.method manual ipv4.addresses {{ new_addr }}/{{ prefix }} ipv4.gateway {{ gateway }} ipv4.dns {{ dns }}
  - shell: sleep 1; ifdown {{ nic }} && ifup {{ nic }}
    async: -1
    poll: 0
  - block:
    - lineinfile: dest=/etc/hosts backrefs=yes regexp='^S+( .*{{ inventory_hostname | regex_escape }}.*)$' line='{{ new_addr }}1'
    - wait_for: host={{ new_addr }} port=22 delay=1 timeout=50
    delegate_to: localhost
  - (後続処理)

ここではAnsible実行側の/etc/hostsを使っている。処理対象のIPアドレス変更後に/etc/hostsも変更して、継続して処理ができるようしたわけだ。
.ssh/configなどを使っても同様のことができるだろう。

実際には

しかし一見上手く行きそうなこの方法だが、実際には大抵の環境で上手く行かない。

それは何故か。
AnsibleはSSH接続を行う時、標準設定だとControlMasterを使用している。ControlPersist=60sなので1分の間は接続先を覚えている。
従って如何に/etc/hostsなどを変更したとしても接続しに行く先はなお変更前のまま、というわけだ。

それではどうすれば良いか。
一例として接続元の残っているSSHプロセスをkillしてやるのが良い。
Ansibleの使っているControlPathは$HOME/.ansible/cp/ansible-ssh-%h-%p-%r(%h,%p,%rはそれぞれ対象ホスト、対象ポート、対象ユーザ)なので以下のようにすれば良いだろう。
このやり方だとAnsible実行側で同じユーザが2つ以上Ansibleを実行していると良くないことが起こりそうだが、それはないこととしておく。

- hosts: all
  tasks:
  - (前処理)
  - command: nmcli connection modify {{ nic }} ipv4.method manual ipv4.addresses {{ new_addr }}/{{ prefix }} ipv4.gateway {{ gateway }} ipv4.dns {{ dns }}
  - shell: sleep 1; ifdown {{ nic }} && ifup {{ nic }}
    async: -1
    poll: 0
  - block:
    - command: pkill -f {{ lookup('pipe', 'echo $HOME') }}/.ansible/cp/ansible-ssh-
      ignore_errors: yes
      run_once: yes
    - lineinfile: dest=/etc/hosts backrefs=yes regexp='^S+( .*{{ inventory_hostname | regex_escape }}.*)$' line='{{ new_addr }}1'
    - wait_for: host={{ new_addr }} port=22 delay=1 timeout=50
    delegate_to: localhost
  - (後続処理)

なお、pkillのところは実害はあまりないがshellモジュールではエラーになったのでcommandモジュールを使っている。(理由を調べてはいない)

おまけ:nmcliモジュールなどここまで辿り着くまでの色々

この文章を書くのにnmcliモジュールを使おうとしたらipv4.methodを変更する方法がなくdhcpからautoにできなかったので没。

nmcliモジュールが使えるようになるまでも一苦労だったし。試行錯誤の結果、操作される側でyumで(インストールされていなければ)NetworkManager-glib, dbus-python, pygobject2をインストールし、pipでpython-nmcliをインストールするのが一番楽っぽい。

最初Fedora 23でやろうとしたらPython 3が標準になったせいでdnfモジュールすら動かないし。python2-dnfをインストールする必要があるのだが、dnfモジュールが使えないのでrawモジュールを使わなければならない。
なお、Fedora 23だと上記のnmcliモジュールに必要なpygobject2はpython-gobjectという名前に変わっている。

TOP