NMState で宣言的にネットワーク設定する

 [NMState](https://nmstate.io/) を用いて、ネットワーク設定を yaml から宣言的に行う。

## 検証環境
- OS: Rocky Linux 9.4

## Installation

### Fedora 系 OS の場合
dnf からインストールする：
```
$ sudo dnf install nmstate 
```

### Fedoar 系以外の OS の場合
Fedora 系 OS 以外はソースからビルドする。手順は公式ドキュメントに従う：

- [nmstate | A Declarative API for Host Network Management](https://nmstate.io/user/install.html#install-from-source)
- [nmstate/CONTRIBUTING.md at base · nmstate/nmstate](https://github.com/nmstate/nmstate/blob/base/CONTRIBUTING.md#installing-and-compiling)

### Build
以下に Ubuntu でのビルド手順を説明する。

ビルドには `cargo`, `git` を用いるので導入しておく：
```
$ sudo apt update && sudo apt install cargo git
```

https://github.com/nmstate/nmstate/releases から `nmstate-<version>.tar.gz` をダウンロードして解凍するか、GitHub レポジトリをクローンする：

```
$ git clone git@github.com:nmstate/nmstate.git
```

ビルドを実行する：
```
$ cd nmstate
$ make
...
```

生成されたバイナリを実行する：
```
$ ./rust/target/release/nmstatectl version
[2024-06-06T02:47:50Z INFO  nmstatectl] Nmstate version: 2.2.32
nmstatectl 2.2.32

# Optional
$ sudo cp ./rust/target/release/nmstatectl $HOME/.local/bin
```

## 作成例
### 例1: Linux bridge の作成

https://nmstate.io/examples.html#interfaces-ethernet に従い、Linux bridge インタフェースを作成する。`br0` という名前のブリッジ・ネットワークインタフェースを作成し、静的IPを割り当てる。
（`ipv4.address`, `bridge.port[].name` の値は、使用する環境に応じて適宜変更する。）

次の内容の `br0.yaml` を作成する。
```yaml:br0.yaml
interfaces:
  - name: br0
    type: linux-bridge
    state: up
    ipv4:
      enabled: true
      address:
        - ip: 192.168.1.10
          prefix-length: 24
      dhcp: false
    ipv6:
      enabled: false
    bridge:
      port:
        - name: eth0   # physical network interface

dns-resolver:
  config:
    server:
    - 8.8.8.8   # DNS server

routes:
  config:
    - destination: 0.0.0.0/0
      next-hop-address: 192.168.1.1   # Gateway
      next-hop-interface: br0
```

次のコマンドを実行し、ネットワークインタフェースを作成する：
```
$ sudo nmstatectl apply br0.yaml
```

結果を確認する：
```
$ ip a
10: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel master br0 state DOWN group default qlen 1000
    link/ether 00:0e:c6:eb:f6:40 brd ff:ff:ff:ff:ff:ff
11: br0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 9a:f3:07:24:da:ab brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.10/24 brd 192.168.1.255 scope global noprefixroute br0
       valid_lft forever preferred_lft forever
```


### インタフェース の無効化・削除
インタフェースの状態を変更させる場合は、yaml ファイルの`interfaces[].state` キーを変更し、再度  `sudo nmstatectl apply <yaml-path>` を実行すればよい。

例えば、インタフェースを down させるには `state: down` を、削除する場合は、`state: absent` を設定し、再度、
```
sudo nmstatectl apply br0.yaml
```
を実行する。

### 例2: Open vSwitch bridge の作成

手順は https://nmstate.io/examples.html#interfaces-ovs-bridge に従う。

:::note warn
本節の内容は Fedora 系以外の OS でやるのは難しそうです。

<details><summary>詳細</summary>

少なくとも Debian 系だと非常に難しそう。NMState で OVS を操作するには `NetworkManager-ovs` plugin が必要だが、これに相当する ppa package が存在しない。また Ubuntu の OVS support に関するチケット：
[Bug #1877884 “OpenVSwitch Support: no package for `nm-openvswitc...” : Bugs : network-manager package : Ubuntu](https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1877884) は 2020年から Wishlist に入ったまま更新がないように見える。
</details>
:::

OpenvSwitch および `NetworkManager-ovs` がない場合は導入する（詳細は次の記事：[Open vSwitch のセットアップ](https://qiita.com/sawa2d2/items/46216f81cff579155c84#open-vswitch-%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)章も参照）：

```
sudo dnf install -y centos-release-openstack-caracal
sudo dnf install -y openvswitch
sudo systemctl enable openvswitch
sudo systemctl start ovs-vswitchd
sudo ovs-vsctl show
sudo dnf install -y NetworkManager-ovs
sudo systemctl restart NetworkManager
```

次の内容の `ovsbr0.yaml` を作成し、`ovsbr0` という名前の OVS ブリッジを作成する。

```yaml:ovsbr0.yaml
interfaces:
- name: br0
  type: ovs-interface
  state: up
  ipv4:
    enabled: true
    address:
      - ip: 192.168.1.10
        prefix-length: 24
- name: ovsbr0
  type: ovs-bridge
  state: up
  bridge:
    options:
      stp: true
    port:
    - name: br0    # Bridge interface
    - name: eth0   # Physical network interface

dns-resolver:
  config:
    server:
    - 8.8.8.8   # DNS server

routes:
  config:
    - destination: 0.0.0.0/0
      next-hop-address: 192.168.1.1   # Gateway
      next-hop-interface: br0
```

次のコマンドを実行し、OVS ブリッジを作成する：
```
$ sudo nmstatectl apply ovsbr0.yaml
```

結果を確認する：
```
$ ovs-vsctl show
438fc2a8-716d-4fd7-927b-e29bf63709cc
    Bridge ovsbr0
        Port br0
            Interface br0
                type: internal
        Port eth0
            Interface eth0
                type: system
    ovs_version: "3.3.1"
```