OpenStack neutronのVLAN aware VMsとGet me a networkを使ってみた

[Fujitsu Advent Calender](http://qiita.com/advent-calendar/2016/fujitsu)および[OpenStack Advent Calender](http://www.adventar.org/calendars/1739)の記事として使われています。
この記事は個人の見解であり、所属する会社、組織を代表するものではありません。また、間違い等ありましたら指摘していただけると幸いです。

# はじめに
本記事はOpenStack Neutronの以下の機能を実際に使ってみながら
ほんのり調べてまとめたものです。

* **VLAN aware VMs**[^vlanawarevms]
* **Get me a network**[^getmeanetwork]

結論からいうと、どちらの機能も問題なく動作しました。
その際のログや内部構造などに興味があればスクロールしてみてください。
どんな機能なのかパッと知りたい方は、以下のセクション名をクリックしてください。
（さっそく下に答えが見えていますが）

* VLAN aware VMsとは
* Get me a networkとは

# VLAN aware VMsとは
これは**VMインスタンスに対して、単一のvNICを使って複数のVLANタグを引き込めるようにする機能**です。

* 従来VLANタグはVMインスタンスに到達する前にOVS(br-int)で外されていたため、VM側に引き込むことができませんでした(br-int内で内部用のport-VLANが設定されていただけ)
* 複数のnetworkをVMインスタンスに接続するためにはその都度vNICを追加(hot attach)する必要があり、スケーラビリティに課題がありました。

本機能が搭載されたことにより

* VM内に複数のコンテナを搭載して、それぞれのコンテナを独立したnetworkにつなげたい。
* VMをルータとして動かす

などが可能になります。

## 環境構築

時間がなかったので **devstack**を用意しました。[^devstack]
今回使ったマシン(VM)のスペックは以下:

* OS

  ```
$ lsb_release -a

No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.1 LTS
Release:        16.04
Codename:       xenial
  ```
* CPU: 2cores
* メモリ: 4GB[^mysql]
* HDD: 30GB
* その他必要なこと
    1. gitとopenvswitchをインストールしておく

        ```
        sudo apt install git openvswitch-switch
        ```
    1. sudo権限を持った``stack``ユーザを作っておく

        ```
        sudo adduser stack
        sudo gpasswd -a stack sudo
        ```

devstack環境構築:

1. ``stack``ユーザでログイン
1. devstack を git clone

    ```
git clone https://github.com/openstack-dev/devstack.git
    ```
1. devstack/local.confを作成。以下のように編集する

    ```devstack.local.conf
[[local|localrc]]
SERVICE_TOKEN=stack
ADMIN_PASSWORD=stack
DATABASE_PASSWORD=stack
RABBIT_PASSWORD=stack
SERVICE_PASSWORD=$ADMIN_PASSWORD
LOGFILE=$DEST/logs/stack.sh.log
LOGDAYS=2
# 不要なので disableに
disable_service n-net
disable_service tempest
# これが今回の要となるサービス
enable_service q-trunk
# Neutron系のサービス
enable_service q-svc
enable_service q-meta
enable_service q-l3
enable_service q-dhcp
enable_service q-agt
# VLANを使う設定
Q_PLUGIN=ml2
Q_ML2_TENANT_NETWORK_TYPE=vlan
ENABLE_TENANT_VLANS=True
PHYSICAL_NETWORK=physnet
ML2_VLAN_RANGES=physnet:1000:2000
OVS_PHYSICAL_BRIDGE=br-eth1
OVS_BRIDGE_MAPPINGS=public:br-ex,physnet:br-eth1
    ```
1. local.confに定義した ``OVS_PHYSICAL_BRIDGE``と同じOVSを事前に作成しておく

    ```
sudo ovs-vsctl add-br br-eth1
    ```
1. devstackを実行

    ```
./stack.sh
    ```
1. 成功すると下記が出力される

    ```
=========================
DevStack Component Timing
=========================
Total runtime         937
run_process            61
test_with_retry         5
apt-get-update          4
pip_install           140
restart_apache_server  13
wait_for_service       18
apt-get                 7
=========================
This is your host IP address: 192.168.122.178
This is your host IPv6 address: ::1
Horizon is now available at http://192.168.122.178/dashboard
Keystone is serving at http://192.168.122.178/identity/
The default users are: admin and demo
The password: stack
    ```

1. trunk(VLAN aware VMs)の機能が有効になっているか確認

    ```
$ openstack extension list --network | grep trunk
    ```
    * ``trunk``サービスが有効になっていないようです。(local.confの定義不足？)
1. ``trunk``サービスを(手動で)有効化するため、neutron.confを編集

    ```diff:/etc/neutron/neutron.conf
-service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin
+service_plugins = trunk,neutron.services.l3_router.l3_router_plugin.L3RouterPlugin
    ```
1. q-svc(neutron-server)を再起動
    * ``screen -r``で ``q-svc``のセッションに入ってシグナルを入れて
      再起動(コマンドを再実行)
1. 再度コマンド実行。 **trunk**および**trunk-details**が見つかれば成功

    ```
$ openstack extension list --network | grep trunk
| Trunk port details                            | trunk-details             | Expose trunk port details                                                                                                                                |
| Trunk Extension                               | trunk                     | Provides support for trunk ports
    ```
1. 自動作成されたリソース(network/subnet/port/router)を全て削除しておく

## 調査

3つのnetwork(VLAN)をVMインスタンスに引き込む構成を作ってみました。

```
                      +--------------------------------------------+
                      |                                            |
                      |            +--------------------+     +----|---+
                      |        +---| network1 VLAN:1031 |-----+        |
 +----------+         |        |   +--------------------+     |        |
 |          |         |        |                              |        |
 |          |   +-----+----+   |   +--------------------+     |        |
 |       vNIC---|  trunk   |---+---| network2 VLAN:1090 |-----+ Router |
 |   VM     |   +----------+   |   +--------------------+     |        |
 | instance |     VLAN:1052    |                              |        |
 +----------+                  |   +--------------------+     |        |
                               +---| network3 VLAN:1048 |-----+        |
                                   +--------------------+     +---------
```

1. 4つのnetwork/subnetを作成(一部出力結果は省略)

    ```
neutron net-create net1; neutron subnet-create net1 192.168.10.0/24 --name subnet1
neutron net-create net2; neutron subnet-create net2 192.168.20.0/24 --name subnet2
neutron net-create net3; neutron subnet-create net3 192.168.30.0/24 --name subnet3
neutron net-create trunk; neutron subnet-create trunk 192.168.100.0/24 --name trunk-subnet
neutron net-list -c name -c provider:network_type -c provider:segmentation_id
+-------+-----------------------+--------------------------+
| name  | provider:network_type | provider:segmentation_id |
+-------+-----------------------+--------------------------+
| net2  | vlan                  |                     1090 |
| trunk | vlan                  |                     1052 |
| net1  | vlan                  |                     1031 |
| net3  | vlan                  |                     1048 |
+-------+-----------------------+--------------------------+
    ```

1. Routerを作成し、全てのsubnetと接続させておく

    ```
neutron router-create router1
neutron router-interface-add router1 subnet1
neutron router-interface-add router1 subnet2
neutron router-interface-add router1 subnet3
neutron router-interface-add router1 trunk-subnet
    ```
1. 束ねられる側のportを3つ作成(出力結果は省略)

    ```
openstack port create --network net1 port-net1
openstack port create --network net2 port-net2
openstack port create --network net3 port-net3
    ```
1. trunk用(束ねる側)のport作成(出力結果は省略)

    ```
openstack port create --network trunk my-trunk
    ```
1. trunkの定義を作成(my-trunkをtrunk用のportとする)

    ```
openstack network trunk create --parent-port my-trunk trunk-net
+-----------------+--------------------------------------+
| Field           | Value                                |
+-----------------+--------------------------------------+
| admin_state_up  | UP                                   |
| created_at      | 2016-12-11T01:18:21Z                 |
| description     |                                      |
| id              | 3b9bb4f7-ae10-477e-9178-e870f134e3f0 |
| name            | trunk-net                            |
| port_id         | 027824df-170d-4f5b-9619-daa3373255a2 |
| project_id      | 598bd1c3dd904b4fbe48ac8b05a0cd01     |
| revision_number | 1                                    |
| status          | DOWN                                 |
| sub_ports       |                                      |
| tenant_id       | 598bd1c3dd904b4fbe48ac8b05a0cd01     |
| updated_at      | 2016-12-11T01:18:21Z                 |
+-----------------+--------------------------------------+
    ```
1. trunkのsubportとして先ほどの3つのportを設定する

    ```
openstack network trunk set trunk-net \
--subport port=port-net1,segmentation-type=vlan,segmentation-id=1100 \
--subport port=port-net2,segmentation-type=vlan,segmentation-id=1200 \
--subport port=port-net3,segmentation-type=vlan,segmentation-id=1300
    ```
    * VMインスタンス側に引き込むVLANIDを指定できます。
1. 現在の設定状況を確認する

    ```
openstack network trunk show trunk-net
+-----------------+--------------------------------------------------------------------------------------------------+
| Field           | Value                                                                                            |
+-----------------+--------------------------------------------------------------------------------------------------+
| admin_state_up  | UP                                                                                               |
| created_at      | 2016-12-11T01:18:21Z                                                                             |
| description     |                                                                                                  |
| id              | 3b9bb4f7-ae10-477e-9178-e870f134e3f0                                                             |
| name            | trunk-net                                                                                        |
| port_id         | 027824df-170d-4f5b-9619-daa3373255a2                                                             |
| project_id      | 598bd1c3dd904b4fbe48ac8b05a0cd01                                                                 |
| revision_number | 2                                                                                                |
| status          | DOWN                                                                                             |
| sub_ports       | port_id='867d7f27-8da0-4dfc-8d37-82e9f1db5f97', segmentation_id='1100', segmentation_type='vlan' |
|                 | port_id='dd79c326-2a73-4d66-a6d5-a71aaefd3948', segmentation_id='1200', segmentation_type='vlan' |
|                 | port_id='e7837b32-b51a-48c8-a4ee-18195b1d336d', segmentation_id='1300', segmentation_type='vlan' |
| tenant_id       | 598bd1c3dd904b4fbe48ac8b05a0cd01                                                                 |
| updated_at      | 2016-12-11T01:20:54Z                                                                             |
+-----------------+--------------------------------------------------------------------------------------------------+
trunk-netのsubportとして3つのportが登録されているようなので、想定通り
    ```
1. VM用のイメージおよび鍵作成
    * デフォルトで入っているcirrosは vconfigが使えませんでした。(パスは通っている...)
    * diskimage-builderでサクッとubuntuのイメージを作ります

    ```
# diskimage-builderのインストール
sudo pip install diskimage-builder
export DIB_DEV_USER_USERNAME='ubuntu'
export DIB_DEV_USER_PASSWORD='ubuntu'
disk-image-create ubuntu vm devuser -o my-ubuntu
# Keypairの登録
nova keypair-add ubuntu --pub-key id_rsa.pub
# Glanceにイメージの登録
openstack image create  ubuntu --disk-format qcow2 --container-format bare --public --file my-ubuntu.qcow2
    ```
1. SecurityGroupの設定(外部からのSSHとicmpを通す)

    ```
neutron security-group-rule-create 45267cfb-1385-47d6-bd62-941c4f6612d7 --direction ingress --port-range-min 22 --port-range-max 22 --protocol tcp --remote-ip-prefix 0.0.0.0/0
neutron security-group-rule-create 45267cfb-1385-47d6-bd62-941c4f6612d7 --direction ingress --protocol icmp --remote-ip-prefix 0.0.0.0/0
    ```
1. VMインスタンスの配備。``--nic``の引数にはtrunk用のport(``my-trunk``)を指定します(出力結果は省略)

    ```
nova boot vlan-aware-vm --flavor d1 --image b360b1af-2bf3-4276-9852-e7ee5832c9cc --nic port-id=027824df-170d-4f5b-9619-daa3373255a2 --key-name ubuntu
nova list
+--------------------------------------+---------------+--------+------------+-------------+---------------------+
| ID                                   | Name          | Status | Task State | Power State | Networks            |
+--------------------------------------+---------------+--------+------------+-------------+---------------------+
| 65909ef2-1b7b-40f4-9bce-1ca44dc1d059 | vlan-aware-vm | ACTIVE | -          | Running     | trunk=192.168.100.4 |
+--------------------------------------+---------------+--------+------------+-------------+---------------------+
    ```
ようやく調査環境の出来上がりです。ホスト上/VM内部から見ていきましょう。

### ホスト上の構成

OVSの構成を確認します。

```
sudo ovs-vsctl list-br

br-eth1
br-ex
br-int
br-tun
tbr-3b9bb4f7-a
```

``tbr-3b9bb4f7-a``が新たに作成されているようです。
以下のコマンドを使って接続関係を調査しました。

```
ovs-vsctl show
brctl show
virsh dumpxml <domain>
ovs-ofctl show
ip link show type veth
```

以下に結果をまとめます。

表. Network/SubnetとVLANIDとの対応関係

|Network名|Subnet            | 実際のVLANID | 内部用VLANID | VM内に届くVLANID |
|---------|------------------|--------------|--------------|------------------|
|net1     | 192.168.10.0/24  | 1031         |1             |1100              |
|net2     | 192.168.20.0/24  | 1090         |2             |1200              |
|net3     | 192.168.30.0/24  | 1048         |3             |1300              |
|trunk    | 192.168.100.0/24 | 1052         |4             |なし              |

```
                 +--------------------------------+
                 |      VM(vlan-aware-vm)         |
                 |                                |
                 |                                |
                 +--------------|-----------------+
                       |  tap027824df-17 |
                       +--------+--------+
                                |
                 +--------------+-----------------+
                 |  qbr027824df-17 (linuxbridge)  |
                 +--------------+-----------------+
                                |
                       +--------|--------+
                       |  qvb027824df-17 |veth peer
                       +--------|--------+
                                |
                       +--------|--------+
                       |  qvo027824df-17 |veth peer
+-------------------------------+-----------------------------------+
|                              #1                                   |
|                                                                   |
|                     tbr-3b9bb4f7-a (openvswitch)                  |
|                                                                   |
|     #2         #3         #4         #5                           |
+------+----------+----------+----------+---------------------------+
    tpt-027    spt-867    spt-dd7    spt-e78
       |       tag:1100   tag:1200   tag:1300
       |          |          |          |       DHCP      DHCP      DHCP       DHCP
       |          |          |          |      (net1)    (net2)    (net3)     (trunk)
       |          |          |          |         |         |         |         |
       |          |          |          |         |         |         |         |
     tag:4      tag:1      tag:2      tag:3     tag:1     tag:2     tag:3     tag:4
    tpi-027    spi-867    spi-dd7    spi-e78   tap407b   tap6514   tapa92f   tap1cf
+------+----------+----------+----------+---------|---------|---------|---------|---+
|     #28        #16        #17        #18       #8        #9        #10       #11  |
|                                                                                   |
|                         br-int (openvswitch)                                      |
|                                                                                   |
| #1             #12        #13        #14      #27            #3              #2   |
+--|--------------+----------+----------+--------+--------------|---------------|---+
int-br-ex      qr-2c9     qr-3d7      qr-cee   qr-ecf       patch-tun     int-br-eth1
   |            tag:1      tag:2      tag:3    tag:4            |               |
   |              |          |          |        |              |               |
   |              |          |          |        |          patch-int           |
   |           qr-2c9     qr-3d7      qr-cee   qr-ecf  +--------|-----------+   |
   |           +--|----------|----------|--------|-+   | br-tun(openvswitch)|   |
   |           |              Router               |   +--------------------+   |
   |           |         (network namespace)       |                            |
   |           +-----------------------------------+                            |
phy-br-ex                                                                  phy-br-eth1
+--|-------------------+                                     +------------------|---+
| br-ex (openvswitch)  |                                     | br-eth1 (openvswitch)|
+----------------------+                                     +----------------------+
```

VMとbr-intとの間に新たなOVSが作成されていました。

ログ. 各OVS上のフローテーブル(コメント付き)

```
sudo ovs-ofctl dump-flows br-eth1
NXST_FLOW reply (xid=0x4):
 cookie=0x9340811c311457d0, duration=6454.065s, table=0, n_packets=2, n_bytes=180, idle_age=6453, priority=4,in_port=1,dl_vlan=1 actions=mod_vlan_vid:1031,NORMAL
 cookie=0x9340811c311457d0, duration=6442.165s, table=0, n_packets=3, n_bytes=222, idle_age=6203, priority=4,in_port=1,dl_vlan=2 actions=mod_vlan_vid:1090,NORMAL
 cookie=0x9340811c311457d0, duration=6434.023s, table=0, n_packets=2, n_bytes=132, idle_age=6199, priority=4,in_port=1,dl_vlan=3 actions=mod_vlan_vid:1048,NORMAL
 cookie=0x9340811c311457d0, duration=6394.064s, table=0, n_packets=92, n_bytes=4768, idle_age=5359, priority=4,in_port=1,dl_vlan=4 actions=mod_vlan_vid:1052,NORMAL
 cookie=0x9340811c311457d0, duration=6764.132s, table=0, n_packets=87, n_bytes=7430, idle_age=5478, priority=2,in_port=1 actions=drop
 cookie=0x9340811c311457d0, duration=6764.156s, table=0, n_packets=0, n_bytes=0, idle_age=39005, priority=0 actions=NORMAL

  ↑内部用のVLANID → 実際のVLANIDに変換して転送、それ以外は全て破棄する

sudo ovs-ofctl dump-flows br-int

NXST_FLOW reply (xid=0x4):
 cookie=0x8386294fbb646d84, duration=200.142s, table=0, n_packets=0, n_bytes=0, idle_age=202, priority=10,icmp6,in_port=28,icmp_type=136 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=196.172s, table=0, n_packets=0, n_bytes=0, idle_age=196, priority=10,icmp6,in_port=30,icmp_type=136 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=195.993s, table=0, n_packets=0, n_bytes=0, idle_age=195, priority=10,icmp6,in_port=31,icmp_type=136 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=195.782s, table=0, n_packets=0, n_bytes=0, idle_age=195, priority=10,icmp6,in_port=29,icmp_type=136 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=200.133s, table=0, n_packets=0, n_bytes=0, idle_age=202, priority=10,arp,in_port=28 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=196.165s, table=0, n_packets=0, n_bytes=0, idle_age=196, priority=10,arp,in_port=30 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=195.974s, table=0, n_packets=0, n_bytes=0, idle_age=195, priority=10,arp,in_port=31 actions=resubmit(,24)
 cookie=0x8386294fbb646d84, duration=195.758s, table=0, n_packets=0, n_bytes=0, idle_age=195, priority=10,arp,in_port=29 actions=resubmit(,24)

  ↑actions=resubmit([port], [table])は、指定された所のルールを再評価するということ
   arpが届いたら table #24のルールを再評価する

 cookie=0x8386294fbb646d84, duration=49386.294s, table=0, n_packets=31, n_bytes=4334, idle_age=27054, priority=2,in_port=1 actions=drop
 cookie=0x8386294fbb646d84, duration=49386.023s, table=0, n_packets=0, n_bytes=0, idle_age=65534, priority=2,in_port=2 actions=drop

  ↑内部用VLANIDが付与されていないものは全てDROP

 cookie=0x8386294fbb646d84, duration=200.153s, table=0, n_packets=2, n_bytes=396, idle_age=199, priority=9,in_port=28 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=196.182s, table=0, n_packets=0, n_bytes=0, idle_age=196, priority=9,in_port=30 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=196.006s, table=0, n_packets=0, n_bytes=0, idle_age=196, priority=9,in_port=31 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=195.795s, table=0, n_packets=0, n_bytes=0, idle_age=195, priority=9,in_port=29 actions=resubmit(,25)

  ↑actions=resubmit([port], [table])は、指定された所のルールを再評価するということ
   #30, 31, 29からのパケットはtable #25のルールを再評価する

 cookie=0x8386294fbb646d84, duration=49075.947s, table=0, n_packets=0, n_bytes=0, idle_age=49075, priority=3,in_port=2,dl_vlan=1031 actions=mod_vlan_vid:1,NORMAL
 cookie=0x8386294fbb646d84, duration=49064.048s, table=0, n_packets=0, n_bytes=0, idle_age=49064, priority=3,in_port=2,dl_vlan=1090 actions=mod_vlan_vid:2,NORMAL
 cookie=0x8386294fbb646d84, duration=49055.894s, table=0, n_packets=0, n_bytes=0, idle_age=49055, priority=3,in_port=2,dl_vlan=1048 actions=mod_vlan_vid:3,NORMAL
 cookie=0x8386294fbb646d84, duration=49015.947s, table=0, n_packets=0, n_bytes=0, idle_age=49015, priority=3,in_port=2,dl_vlan=1052 actions=mod_vlan_vid:4,NORMAL

  ↑#2から届いたパケットに対して、実際の(外部に見える)VLAN → 内部用VLANの変換をして転送

 cookie=0x8386294fbb646d84, duration=49386.596s, table=0, n_packets=2233, n_bytes=181970, idle_age=1, priority=0 actions=NORMAL
 cookie=0x8386294fbb646d84, duration=49386.594s, table=23, n_packets=0, n_bytes=0, idle_age=65534, priority=0 actions=drop

  ↑いずれもマッチしない場合は全てDROP

 cookie=0x8386294fbb646d84, duration=200.147s, table=24, n_packets=0, n_bytes=0, idle_age=200, priority=2,icmp6,in_port=28,icmp_type=136,nd_target=fe80::f816:3eff:fe31:3980 actions=NORMAL
 cookie=0x8386294fbb646d84, duration=196.177s, table=24, n_packets=0, n_bytes=0, idle_age=196, priority=2,icmp6,in_port=30,icmp_type=136,nd_target=fe80::f816:3eff:febe:dc65 actions=NORMAL
 cookie=0x8386294fbb646d84, duration=195.999s, table=24, n_packets=0, n_bytes=0, idle_age=195, priority=2,icmp6,in_port=31,icmp_type=136,nd_target=fe80::f816:3eff:fefb:b50a actions=NORMAL
 cookie=0x8386294fbb646d84, duration=195.788s, table=24, n_packets=0, n_bytes=0, idle_age=195, priority=2,icmp6,in_port=29,icmp_type=136,nd_target=fe80::f816:3eff:fea6:851a actions=NORMAL
 cookie=0x8386294fbb646d84, duration=200.136s, table=24, n_packets=0, n_bytes=0, idle_age=200, priority=2,arp,in_port=28,arp_spa=192.168.100.4 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=196.168s, table=24, n_packets=0, n_bytes=0, idle_age=196, priority=2,arp,in_port=30,arp_spa=192.168.20.4 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=195.989s, table=24, n_packets=0, n_bytes=0, idle_age=195, priority=2,arp,in_port=31,arp_spa=192.168.30.4 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=195.775s, table=24, n_packets=0, n_bytes=0, idle_age=195, priority=2,arp,in_port=29,arp_spa=192.168.10.7 actions=resubmit(,25)
 cookie=0x8386294fbb646d84, duration=49386.593s, table=24, n_packets=18, n_bytes=756, idle_age=41435, priority=0 actions=drop

  ↑trunkおよびsubportに指定したIPアドレスと一致する場合は、更に table #25を評価
  一致しなければ全てDROP

 cookie=0x8386294fbb646d84, duration=200.161s, table=25, n_packets=0, n_bytes=0, idle_age=202, priority=2,in_port=28,dl_src=fa:16:3e:31:39:80 actions=NORMAL
 cookie=0x8386294fbb646d84, duration=196.192s, table=25, n_packets=0, n_bytes=0, idle_age=196, priority=2,in_port=30,dl_src=fa:16:3e:be:dc:65 actions=NORMAL
 cookie=0x8386294fbb646d84, duration=196.017s, table=25, n_packets=0, n_bytes=0, idle_age=196, priority=2,in_port=31,dl_src=fa:16:3e:fb:b5:0a actions=NORMAL
 cookie=0x8386294fbb646d84, duration=195.808s, table=25, n_packets=0, n_bytes=0, idle_age=195, priority=2,in_port=29,dl_src=fa:16:3e:a6:85:1a actions=NORMAL

  ↑trunkおよびsubportに指定したportのMACアドレスと一致する場合は転送
  一致しなければ全てDROP
```

### ゲスト上の構成

VMインスタンスにログインします。一旦ルータ(namespace)に入った後でSSHログインします。

```
ip netns
qrouter-d282be23-d5fa-4080-95d9-e087854c9b6a
qdhcp-00ceeee0-f72e-441b-9c3e-f398b3ded30f
qdhcp-a5620b9c-70f6-4b8f-953a-b54730c014a2
qdhcp-a8e2324b-38a7-4780-9a61-b5b99500a32e
qdhcp-4f3bf5ff-4a68-42b6-93a1-58ce3ea7fba8

sudo ip netns exec qrouter-d282be23-d5fa-4080-95d9-e087854c9b6a bash
ssh -i ubuntu_sshkey/id_rsa ubuntu@192.168.100.4
bash
```
* なぜかデフォルトがshだったためbashを呼び出しています

NICの構成を確認してみます。

```
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether fa:16:3e:31:39:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.4/24 brd 192.168.100.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe31:3980/64 scope link
       valid_lft forever preferred_lft forever
```
通常の配備と比べて変化はないようです。
とはいえ、VM内に VLAN1100, 1200, 1300 のタグ付きパケットが届くはずなので
確認のためVLAN I/Fを定義します。(結果は省略)

```
sudo vconfig add ens3 1100; sudo vconfig add ens3 1200; sudo vconfig add ens3 1300
sudo ifconfig ens3.1100 192.168.10.7/24
sudo ifconfig ens3.1200 192.168.20.4/24
sudo ifconfig ens3.1300 192.168.30.4/24
ip a | grep -2 192.168

2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether fa:16:3e:31:39:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.4/24 brd 192.168.100.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe31:3980/64 scope link
--
3: ens3.1100@ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fa:16:3e:31:39:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.7/24 brd 192.168.10.255 scope global ens3.1100
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe31:3980/64 scope link
--
4: ens3.1200@ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fa:16:3e:31:39:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.4/24 brd 192.168.20.255 scope global ens3.1200
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe31:3980/64 scope link
--
5: ens3.1300@ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fa:16:3e:31:39:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.4/24 brd 192.168.30.255 scope global ens3.1300
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe31:3980/64 scope link
```
ルータ内から適当にpingを飛ばして、そのパケットをVM内でキャプチャしてみます。
VLANタグが付与されているか確認します。

```
VlAN1100(192.168.10.7)宛
tcpdump -i ens3.1100 -e -n
  ...(snip)...
  18:36:12.855988 fa:16:3e:19:53:38 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 1100, p 0, ethertype ARP, Request who-has 192.168.10.7 tell 192.168.10.1, length 28
  18:36:12.856145 fa:16:3e:31:39:80 > fa:16:3e:19:53:38, ethertype 802.1Q (0x8100), length 46: vlan 1100,ｋｊｊ p 0, ethertype ARP, Reply 192.168.10.7 is-at fa:16:3e:31:39:80, length 28

VlAN1200(192.168.20.4)宛
tcpdump -i ens3.1200 -e -n
  ...(snip)...
  18:49:10.673920 fa:16:3e:69:2a:0c > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 1200, p 0, ethertype ARP, Request who-has 192.168.20.4 tell 192.168.20.1, length 28
  18:49:10.674406 fa:16:3e:31:39:80 > fa:16:3e:69:2a:0c, ethertype 802.1Q (0x8100), length 46: vlan 1200, p 0, ethertype ARP, Reply 192.168.20.4 is-at fa:16:3e:31:39:80, length 28

VlAN1300(192.168.30.4)宛
tcpdump -i ens3.1300 -e -n
  ...(snip)...
  18:50:27.918014 fa:16:3e:fe:ff:3f > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 1300, p 0, ethertype ARP, Request who-has 192.168.30.4 tell 192.168.30.1, length 28
  18:50:27.918441 fa:16:3e:31:39:80 > fa:16:3e:fe:ff:3f, ethertype 802.1Q (0x8100), length 46: vlan 1300, p 0, ethertype ARP, Reply 192.168.30.4 is-at fa:16:3e:31:39:80, length 28
```
VMまでVLANタグが届いていることを確認できました。[^myhome]
次にSecurity-group(SG)ですが、trunkとして指定したportのSGのみが作用していました。
trunkにまとめたVLANタグ通信ごとにSGを適用することはできないため
VMゲスト内で設定する必要がありそうです。

# Get me a networkとは

**VMインスタンス配備の簡易化機能**です。
動作的には、Neutronのリソースを自動的に作ってVMインスタンスと接続してくれるため
利用者はNeutronのリソースを用意せずにVMインスタンスを配備できるようになりました。

従来、プロジェクト利用者が外部通信可能なVMインスタンスを配備するためには
少なくとも以下の手順が必要であり、ただVMだけ立ち上げたいユーザにとっては面倒だという
意見があったようです。

1. private network作成
1. private subnet作成
1. router作成
1. routerにprivate subnetを接続
1. routerにexternal networkを接続

> Neutron以前のNova-networkでは、上記のような作業は不要でした。

こうした背景から、利用者の負担を減らす仕組みが必要だよねという話になったようです。

## 環境構築

1. 前のセクションと同じ方法で構築後、全てのリソースを削除しておく
1. Get me a network(``Auto Allocated Topology``)が有効になっているかを確認

    ```
openstack extension list --network | grep auto
| Auto Allocated Topology Services              | auto-allocated-topology   | Auto Allocated Topology Services
    ```

## ゼロから始めるGet me a network

~~タイトル回収のため~~Neutronのリソースが一つもない状態から始めます。

* VMインスタンスの配備(出力結果は省略)

    * --nic **auto** というパラメータがトリガーです

    ```
nova boot --flavor 1 --image 198384a2-9ece-4d24-a509-4ce02f183b63 vm1 --nic auto
    ```

ここでエラーが発生しました。

```python:stdout
ERROR (BadRequest): Unable to automatically allocate a network for project 74df629a6bb04002be77a3ff998fb0a0 (HTTP 400) (Request-ID: req-ecbf665b-9563-46a4-b95a-122fb22646fa)
```
上記だけだと理由が分からないので、nova-apiのログを確認します(一部抜粋)

```python:/opt/stack/logs/n-api.log
Error message: {"NeutronError": {"message": "Deployment error: No default router:external network.", "type": "AutoAllocationFailure", "detail": ""}}
Unable to auto-allocate networks. Deployment error: No default router:external network.
```
Defaultのrouter:externalなnetworkがないよ、言われています。
もう少し詳しく探るため、エラーが起きたNeutron側の処理まで順を追って確認します。

```
  (command)    +------+   +---------------------------+   +---------+
$ nova boot -->| Nova |-->| Python-neutronclient(CLI) |-->| Neutron |
               +------+   +---------------------------+   +---------+
```
(nova boot ---> 色々と省略 ---\>)

[\_ports_needed_per_instance](https://github.com/openstack/nova/blob/master/nova/network/neutronv2/api.py#L1512)

* 指定されたプロジェクト内でVMインスタンスに付与するためのnetworkを探す。
* 見つかった場合「使えるnetworkを見つけたからそれを指定しろ」というエラーが発生する

[\_can_auto_allocate_network](https://github.com/openstack/nova/blob/master/nova/network/neutronv2/api.py#L1465)

* **Auto Allocated Topology Services**が有効な場合、
  Python-neutronclientの validate_auto_allocated_topology_requirementsを呼び出す

[validate_auto_allocated_topology_requirements](https://github.com/openstack/python-neutronclient/blob/291b31e0e5eed01cbbe917a644e38c2cedafae3e/neutronclient/v2_0/client.py#L1855)

* Neutronの get_auto_allocated_topologyを呼び出す

[get_auto_allocated_topology](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L113)

* 指定されたプロジェクト内でauto_allocate可能かどうかを判断し、可能であればnetwork/router/subnetを自動作成する。
    1. [\_check_requirements](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L176)
        * ``is_default = True``かつ ``router:external = True``のnetworkを探す。なければエラー
        * ``is_default = True``のsubnetpoolを探す。なければエラー
    1. [\_get_auto_allocated_network](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L205)
        ：指定されたプロジェクト内で既にauto_allocated_networkがある場合はそれを使う。
    1. [\_get_default_external_network](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L220)
        ：``is_default=True``かつ ``router:external=True``なnetworkを探す。存在しなければエラー
    1. [\_build_topology](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L154)

        1. [\_provision_tenant_private_network
](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L257)
            * network/subnetを自動作成
        1. [\_provision_external_connectivity](https://github.com/openstack/neutron/blob/b9d0a5b885282f170952b102a8f69f3112df9406/neutron/services/auto_allocate/db.py#L295)
            * routerを自動作成して、routerと外部ネットワークとを接続。

さきほどは``_check_requirements``のところで
``is_default=True``かつ``router:external=True``のnetworkがなかったためエラーになったようです。

以下にGet me a networkが使える条件についてまとめました。

----
1. ``is_default``かつ ``router:external``のnetworkが存在している かつ
1. ``is_default``のsubnetpoolが自身のプロジェクトから参照可能(IPv4/v6は問わず) かつ
1. 自身のプロジェクト内に 1. を含むnetworkが一つも存在しない

----

この条件下で、nova bootのオプションとして **--nic auto** を指定すると
Get me a network(auto-allocate-topology)が動作します。

ということで、管理者(admin)権限で以下のnetwork/subnet/subnetpoolを作成します。

```
# 管理者(admin)に変更
source ~/devstack/openrc admin admin

neutron net-create external --router:external --is_default
neutron subnetpool-create --is_default --shared --pool-prefix 10.0.0.0/20 --default-prefixlen 24 default_subnetpool
neutron subnet-create external --use-default-subnetpool
```
* subnetpoolは ``shared`` 属性をつけないと他プロジェクトから参照できません[^networkshared]

気を取り直してもう一度

## Re:ゼロから始めるGet me a network

~~これが言いたかっただけです~~

* VMインスタンスの配備(出力結果は省略)

```
# プロジェクト利用者(demo)に変更
source ~/devstack/openrc demo demo

nova boot --flavor 1 --image 198384a2-9ece-4d24-a509-4ce02f183b63 vm1 --nic auto
nova list
+--------------------------------------+------+--------+------------+-------------+---------------------------------+
| ID                                   | Name | Status | Task State | Power State | Networks                        |
+--------------------------------------+------+--------+------------+-------------+---------------------------------+
| 03e7439e-7cdf-4fd3-b0fb-9b5cb234f2b8 | vm1  | ACTIVE | -          | Running     | auto_allocated_network=10.0.1.3 |
+--------------------------------------+------+--------+------------+-------------+---------------------------------+
```
配備に成功しました。以下のように自動作成されていることを確認できました:v_tone1:

![topology.png](https://qiita-image-store.s3.amazonaws.com/0/152668/ae307304-c247-1d40-fa0b-27e4114a447b.png)

* auto_allocated_network
* auto_allocated_subnet_v4
* auto_allocated_router

ついでですが--nic **none** と指定することで 下記のようにnetworkを持たない
VMインスタンスを配備することも可能です。

```
nova boot --flavor 1 --image 2180aed3-ea7c-4bec-a4fa-e61b22cfd64d --nic none vm2
nova list

+--------------------------------------+------+--------+------------+-------------+----------------------------------+
| ID                                   | Name | Status | Task State | Power State | Networks                         |
+--------------------------------------+------+--------+------------+-------------+----------------------------------+
| e36b8446-5c4c-42bb-a80f-834f7a387c31 | vm1  | ACTIVE | -          | Running     | auto_allocated_network=10.0.1.11 |
| 97934c9c-30eb-4d86-8b98-6eebe32db86b | vm2  | ACTIVE | -          | Running     |                                  |
+--------------------------------------+------+--------+------------+-------------+----------------------------------+
```

# まとめ

**VLAN aware VMs**

* 一つのvNICに複数のVLANタグを引き込ませる機能のこと
* VMインスタンスとbr-intとの間に新たなOVSを噛ませてVLANタギング設定
* ``openstack network trunk`` コマンドで設定可能
* 現状はVMインスタンスに対する設定のみなので、将来ベアメタルにも
  適用されることを期待。[^awarebaremetal]

**Get me a network**

* VMインスタンス配備時の設定簡易化機能のこと
* 以下の条件の下、VMインスタンス配備時に --nic **auto** を指定することで動作する。
    1. ``is_default`` かつ ``router:external`` の ``network`` が存在している
    2. ``is_default`` の ``subnetpool``が自身のプロジェクトから参照可能(IPv4/v6は問わず)
    3. 自身のプロジェクト内に 1. を含む``network``が一つも存在しない
* --nic **none**を指定するとネットワークなしの状態で配備可能
* 実際に使ってみて楽だと感じた。
* Get me a network!と言いたくなる（個人差があります）

# おわりに

調査しきれない部分[^myhome]もありましたが
OpenStackは機能が充実して、かなり自由度が上がってきていると感じています。
初投稿なので緊張しました:beginner:

# 参考文献

* http://blog.nedia.ne.jp/2014/12/18/4194.html#i
* https://www.ainoniwa.net/pelican/2014/1102b.html
* http://www.slideshare.net/ritchey98/20161129-neutron-recent-topic
* https://github.com/openstack/neutron/blob/master/doc/source/devref/openvswitch_agent.rst

[^devstack]: 開発向けのOpenStack環境の構築用スクリプト。マシン1台からOpenStackの環境を構築できます(All-in-one)。詳細は[こちら](http://docs.openstack.org/developer/devstack/guides/single-vm.html)
[^mysql]: メモリをケチって2GBで動かしたところ、devstackインストール後にmysql上で``[ERROR] InnoDB: Cannot allocate memory for the buffer pool``が発生しました。^^;
[^vlanawarevms]: SPEC(仕様書)は [こちら](https://specs.openstack.org/openstack/neutron-specs/specs/mitaka/vlan-aware-vms.html)
[^myhome]: 疎通確認用のVM/コンテナを作って色々確認する予定でしたがHDDとメモリ不足で断念。
[^getmeanetwork]: SPEC(仕様書)は [こちら](https://specs.openstack.org/openstack/neutron-specs/specs/liberty/get-me-a-network.html)
[^networkshared]: networkは ``router:external``属性をTrue（外部ネットワーク）にすると他プロジェクトから参照できます。
[^awarebaremetal]: 詳しくは[こちら](https://bugs.launchpad.net/ironic/+bug/1543584)