nmcliコマンドの隅をつつく

# 概要
普通は気にしないような、nmcliコマンドの挙動を調べてみたので共有します。重箱の隅をつつくような内容なので、面白半分で読んでください。

# 結論
- nmcli connectionコマンドでIPアドレスを設定したときは、ip address showで本当に変わっているか確認しましょう。 ([IPアドレスが変えられない](#ipアドレスが変えられない))
- IPアドレスが変わらないときは、[余談 その４](#その４)を試すと確実ですが、オススメしません。connectionの恩恵が受けられないからです。
- connectionはネットワークの設定をコロコロ変えたいときに使うと便利です。 ([余談 その２](#その２))

# 問題提起
nmcliコマンドは、

> nmcli {general | connection | device} [COMMAND] [ARGUMENTS...] 
\(man NMCLI(1)より)

のような構文で実行します。

例えば、

```shell
nmcli connection modify eth0 ipv4.address 192.168.0.2
```
で、eth0インターフェースのIPアドレスを`192.168.0.2`にすることができます。

というのが、よくある解説ですが、僕は、この解説では説明がつかない状況に出くわしました。あの解説は半分正解で半分間違いだったのです！！

回りくどくなるので、結論から先に書くと、間違っている(or 誤解を招く)のは、**"eth0インターフェースに対して操作をしているのではない"**という点です。

# deviceとconnection

ここで、eth0インターフェースとは、

```shell
ip address show

# 1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc pfifo_fast state UP group default qlen 1000
#     inet 192.168.0.2/32 brd 192.168.0.255 scope global eth0
#        valid_lft forever preferred_lft forever
```
で出てくるeth0のことを指しています。

実は、`nmcli connection ...`で使った`eth0`はこれとは別物なのです。このことをはっきりさせるために、次のコマンドを実行してみます。

```shell
nmcli connection show
# NAME  UUID                                  TYPE            DEVICE
# eth0  bc532b37-088f-3de8-a642-840c85dfd04b  802-3-ethernet  eth0
```

このように表示されると思いますが、ここでNAMEとなっているものが`nmcli connection ...`で指定したeth0で、DEVICEとなっているのが`ip address show`で指定したeth0です。実際、

```shell
nmcli connection down eth0
nmcli connection modify eth0 connection.id eth1
nmcli connection up eth1
```

とすると、 

```shell
nmcli connection show
# NAME  UUID                                  TYPE            DEVICE
# eth1  bc553237-088f-3de8-a532-811f7535ea4b  802-3-ethernet  eth0
```

上のようにNAMEが変わります。一方、DEVICEはそのままです。また、eth0のまま先のコマンドを実行すると、

```shell
nmcli connection modify eth0 ipv4.address 192.168.0.2
# Error: unknown connection 'eth0'.
```

と怒られます。

このように、nmcliコマンドにおいてはeth0が二つの物を指しています。`ip address show`で出てくるものを**device**、`nmcli connection ...`で使うものを**connection**と呼び、区別します。

# IPアドレスが変えられない

では、伏線の回収をしていきましょう。さきほど、僕が困った状況に陥ったと書きましたが、これはまさにdeviceとconnectionの微妙な関係によるものでした。

ある日、出来立てほやほやなサーバーのIPアドレスを固定しようと思い、

```shell
nmcli connection down eth0
nmcli connection modify eth0 ipv4.method manual
nmcli connection modify eth0 ipv4.address 192.168.0.2
nmcli connection up eth0
service network-manager restart # いらないかも
```

を実行しました。しかし、この変更がなぜか`ip address show`に反映されませんでした。理由を調べるために、

```shell
nmcli connection show eth0 # eth0 connectionの詳細を見る
```

を実行すると、

```
...
ipv4.addresses:                         192.168.0.2/32
...
IP4.ADDRESS[1]:                         192.168.0.5/32 # 変更前のIP
...
```

という表示が得られました。なぜか変更前のIPアドレスが表示に残っています。そのIPに対応している`IP4.ADDRESS[1]`とは一体何やねん!?

色々調べまくった結果、`nmcli connection show`の表示のうち、大文字のものは、どうやらdeviceに設定されている値のようだとわかりました。

また、上の状態で`ip address show`に表示されるのは192.168.0.5(device)だけなので、**`ip address show`に表示されるのはdeviceの設定のみ**だと分かります。また、192.168.0.2(connectionのみ)にはpingが通らなかったので、**実際の通信に使われる設定はdeviceに反映されたものだけ**だと分かります。

さらに、変更前のIPはdeviceに設定されており、deviceの設定はconnectionの設定で上書きできないということも推測できます(要検証)。

# deviceを設定する

ここで注意しないといけないのは、deviceの設定はconnectionの設定でできないわけではなく、単に上書きができないだけということです。

たとえば、DNSの設定を見てみます。IPアドレスを固定したあとやりたいことはDNSの設定ですよね。

まずは、DNSがdeviceに設定されていないことを確認します。deviceの設定の確認は、

```
nmcli device show eth0
```

で出来ます。結果の中に`IP4.DNS[n]:`のような項目がないことを確認してください。

DNSは、

```shell
nmcli connection down eth0
nmcli connection modify eth0 ipv4.ignore-auto-dns yes # dhcpを無視
nmcli connection modify eth0 ipv4.dns 8.8.8.8
nmcli connection up eth0
```

で設定します。コマンドを実行すると、

```shell
nmcli device show eth0
```

の結果の中に`IP4.DNS[1]: 8.8.8.8`という項目が現れるはずです。つまり、connectionの設定によって、deviceの設定が更新されたのです。

# 複数のconnection
nmcliコマンドでは、自分でconnectionを新たに作ることができます。例えば、

```shell
nmcli connection add con-name hoge ifname eth0 type ethernet
```

で、eth0 deviceに接続されたhogeという名前(connection.id)のconnectionが作られます。

この話を聞いて気になるのは、一つのdeviceに複数のconnectionが接続されたらどうなるのか、ですよね。試してみます。

まず、今のconnectionの状況を確認します。それは、前も出てきた

```shell
nmcli connection show
```

で出来ます。結果は、

```
NAME  UUID                                  TYPE            DEVICE
eth0  bc556437-088f-3348-a832-813045dfd04b  802-3-ethernet  eth0
hoge  2aca727a-5325-4bdf-bffb-4f1413e89ce6  802-3-ethernet  --
```

となりました。どうやらhoge connectionは有効でないようです。では、有効化して見ましょう。

```shell
nmcli connection up hoge
```

もう一度connectionの状況を確認してみると、

```
NAME  UUID                                  TYPE            DEVICE
eth0  bc556437-088f-3348-a832-813045dfd04b  802-3-ethernet  --
hoge  2aca727a-5325-4bdf-bffb-4f1413e89ce6  802-3-ethernet  eth0
```

となりました。次はeth0が有効でなくなりました。どうやら、一つのdeviceに対しては一つのconnectionしか有効化出来ないみたいです。

# 余談

## その１
特定の状況では、デフォルトのconnection名が`Wired connection 1`などのイケてない名前になります。そんなときは、次のコマンドで訂正しましょう。

```shell
nmcli connection modify "Wired connection 1" connection.id eth0
```

覚えていないかもしれませんが、説明の中で`名前(connection.id)`という書き方をしたのは、この`connection.id`にまつわる設定変更への伏線でした。

## その２
connectionというのは、設定の集まりであって、物理的な何かを指すものではなさそう。
> NetworkManager stores all network configuration as "connections", which are collections of data (Layer2 details, IP addressing, etc.) that describe how to create or connect to a network.
(man NMCLI(1) より)

複数のconnectionを一つのdeviceに紐づけるメリットは、以下の引用を参考にしてください。

> Consider a machine which is usually connected to a DHCP-enabled network, but sometimes connected to a testing network which uses static IP addressing. Instead of manually reconfiguring eth0 each time the network is changed, the settings can be saved as two connections which both apply to eth0, one for DHCP (called default) and one with the static addressing details (called testing). When connected to the DHCP-enabled network the user would run nmcli con up default , and when connected to the static network the user would run nmcli con up testing.
(man NMCLI(1)より)

要約すると、connectionってネットワークの設定の集まりでしょ。だから、あらかじめ通常用とテスト用で二つのconnectionを作っとけば、テストしたいときはテスト用の、戻したければ通常用のconnectionを付けるだけでネットワークの設定を済ませることができるから便利だよね、という感じです。

## その３
最後の説明ではconnectionの作り方だけを説明しましたが、削除の仕方を説明しませんでした。connectionを作るだけ作ってみたけど、処分に困っているそこのあなた！朗報です。これから説明します。

```shell
nmcli connection delete hoge
```

これだけです。ここで終わっても良いのですが、もう少しだけお付き合いください。

hogeというconnectionが複数あって、一方だけ消したい。もう片方を消してしまうとサービスもろともネットから消えてしまう。そんな憂鬱な気分のあなた！朗報です。片方だけ消せます。

まずは、各connectionに付けられたUUID(Universally Unique ID)を確認します。

```shell
nmcli connection show
# NAME  UUID                                  TYPE            DEVICE
# hoge  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  802-3-ethernet  eth0
# hoge  yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy  802-3-ethernet  --
```

このように表示されたとしましょう。ここで、UUIDがyyy...yyyのconnectionを消したいときは、

```shell
nmcli connection delete yyy...yyy
```

とすればいいだけです。実は、前にも出てきたUUIDはこんな風に使えるのです。

## その４

で、結局deviceにIPアドレスが設定されている場合はどないしたらええねん!?という疑問に答えるのを忘れてました。

```shell
nmcli device modify eth0 ipv4.address 192.168.0.2
```

とすればOKです。

<お わ り>