NICを削除すると一時的に virtualMachine.guest.net のIDが -1 になる

# TL; DR

- [vSphere Web Services API](https://developer.vmware.com/apis/1192/vsphere)で、vmのNICを削除後10秒程度 `virtualMachine.guest.net` の該当NICに対応する情報が残る
- その際、 `DeviceConfigId` が `-1` になる
- vmのNIC追加時は一時的に `virtualMachine.guest.net` まるごと `null` になる

```json
{
  "Network": "",
  "IpAddress": [
    "fe80::250:56ff:fea1:4650"
  ],
  "MacAddress": "00:50:56:a1:46:50",
  "Connected": true,
  "DeviceConfigId": -1,
  "DnsConfig": null,
  "IpConfig": {
    "IpAddress": [
      {
        "IpAddress": "fe80::250:56ff:fea1:4650",
        "PrefixLength": 64,
        "Origin": "",
        "State": "unknown",
        "Lifetime": null
      }
    ],
    "Dhcp": null,
    "AutoConfigurationEnabled": null
  },
  "NetBIOSConfig": null
}
```

# はじめに

タイトル通りです。[vSphere Web Services API](https://developer.vmware.com/apis/1192/vsphere)で -1のIDを持つ謎NICを調査していたら上記現象を見つけました。

# 検証環境

自宅vCenterを使用しました。

- vcsa 7.0u3
- ESXi 7.0u2
- Photon OS 4.0 (ovaを使用)

~~ESXiのバージョンアップサボってるのがばれる~~

# GuestNicInfo

本題に入る前に、`virtualMachine.guest.net` について。

このプロパティはゲストOSに認識されているNIC情報を返します。ゲストOSの情報なので、VMware Toolsがインストールされていないと取得できません。

> Data Object - GuestNicInfo(vim.vm.GuestInfo.NicInfo)
> Information about each virtual network adapter configured in the guest operating system.

https://developer.vmware.com/apis/1192/vsphere

そして、GuestNicInfoの `deviceConfigId` がデバイスキーに対応しています。

VMのデバイスは `config.hardware.device` で取得できます。こちらはゲストOSに関係なくNIC等が取得できます。
デバイスキーはデバイスごとに一意なIDで、0以上の値をとります。

> Data Object - VirtualDevice(vim.vm.device.VirtualDevice)
> key	xsd:int	
> A unique key that distinguishes this device from other devices in the same virtual machine. ... Persistent device keys are always assigned and managed by the server, which guarantees that **all devices will have non-negative key values**.

`deviceConfigId` がデバイスキーに対応するなら当然0以上になるはずなのに、一時的に-1になってしまうというのが今回の内容です。

# 実験1：VMのNICを削除

VMのNICを削除すると、削除したNICに対応するGuestNicInfoの `deviceConfigId` が-1になります。

まずはVMware Toolsが入ったVM[^1]を起動します。NICは2本刺さっています。

```
govc device.info -vm photon-ova  ethernet-*
Name:               ethernet-0
  Type:             VirtualVmxnet3
  Label:            Network adapter 1
  Summary:          VM Network
  Key:              4000
  Controller:       pci-100
  Unit number:      7
  Connected:        true
  Start connected:  true
  Guest control:    true
  Status:           ok
  MAC Address:      00:50:56:a1:d6:86
  Address type:     assigned
Name:               ethernet-1
  Type:             VirtualVmxnet3
  Label:            Network adapter 2
  Summary:          DVSwitch: 50 21 44 2c 15 ff a7 d2-28 b0 2a c5 2c 26 45 5d
  Key:              4001
  Controller:       pci-100
  Unit number:      8
  Connected:        true
  Start connected:  true
  Guest control:    true
  Status:           ok
  MAC Address:      00:50:56:a1:bc:fd
  Address type:     assigned
```

次にNICを1つ削除します。**削除してもGuestNicInfo側からはすぐには消えず、代わりにDeviceConfigIdが-1になります**。その後さらに十数秒経過するとGuestNicInfoからも削除されます。

```bash
# key 4001 のNICを削除
$ govc device.remove -vm photon-ova ethernet-1
```

```bash
// VirtualDeviceと並べて比較
$ for i in {1..100} ; do echo ---guest--- ; govc vm.info -json photon-ova | jq .VirtualMachines[].Guest.Net[].DeviceConfigId ; echo ---device--- ; govc device.info -json -vm photon-ova ethernet-* | jq .Devices[].Key ; sleep 1 ; done
...
---guest--- # GuestNicInfoのキー一覧
4000
4001
---device--- # VirtualDeviceのキー一覧
4000
4001
---guest---
4000
4001
---device---
4000
4001
---guest--- # NICが消えたタイミングで-1になる
4000
-1
---device---
4000
# ...(11秒後)
---guest--- # GuestNicInfoからも消える
4000
---device---
4000
```

`DeviceConfigId` 以外に `Network` にも差分が生じていました。どこにも繋がっていないことを表現しているのではないかと思われます。

```jsonc:削除前
{
  "Network": "DC0_DVPG0",
  "IpAddress": [
    "fe80::250:56ff:fea1:4650"
  ],
  "MacAddress": "00:50:56:a1:46:50",
  "Connected": true,
  "DeviceConfigId": 4001,
  "DnsConfig": null,
  "IpConfig": {
    "IpAddress": [
      {
        "IpAddress": "fe80::250:56ff:fea1:4650",
        "PrefixLength": 64,
        "Origin": "",
        "State": "unknown",
        "Lifetime": null
      }
    ],
    "Dhcp": null,
    "AutoConfigurationEnabled": null
  },
  "NetBIOSConfig": null
}
```

```jsonc:削除後すぐ
{
  "Network": "", // 空になる
  "IpAddress": [
    "fe80::250:56ff:fea1:4650"
  ],
  "MacAddress": "00:50:56:a1:46:50",
  "Connected": true,
  "DeviceConfigId": -1, // -1になる
  "DnsConfig": null,
  "IpConfig": {
    "IpAddress": [
      {
        "IpAddress": "fe80::250:56ff:fea1:4650",
        "PrefixLength": 64,
        "Origin": "",
        "State": "unknown",
        "Lifetime": null
      }
    ],
    "Dhcp": null,
    "AutoConfigurationEnabled": null
  },
  "NetBIOSConfig": null
}
```

(GuestNicInfoが消えるタイミングは謎でした。ゲストOS内で `ip a` したらVirtualDevice更新と同じタイミングで消えたので、「ゲストOSから認識されなくなるタイミング」でもなさそうです）

# 実験2: VMのNICを追加

次に、NICを追加してみます。今度は **GuestNicInfo全体が一時的に `null` になりました**。追加中は全てのNICが取得できなくなるようです。

```bash
$ govc vm.network.add -vm photon-ova DC0_DVPG0
$ govc vm.info -json photon-ova | jq .VirtualMachines[].Guest.Net
null
```

# 実験3: VMのNICを切断

NICの切断はGuestNicInfoに影響を及ぼしませんでした。（`Connected: false` になるだけ）

# おわりに

以上、`GuestNicInfo` の挙動についてでした。不思議な値が出た際には、削除周りを調べるのがよさそうです。

[^1]:  [Photon OSのVMをOVAから作成](https://github.com/vmware/photon/wiki/Downloading-Photon-OS)すると、VMware Toolsが初めから入っていて楽でした。