JuniperのQFX10kの仮想環境に触れる

[DMM.com #1 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dmm) の6日目を担当させていただきます、@sinnershikiです。
前日の記事は、@nokkiiさんの[Google Homeで作ったスマートホームを自慢したい男たち](https://qiita.com/nokkii/items/4c83889fef4ad7a22b3b)でした。

私が最近何をやっているかといいますと、新米インフラエンジニアとして、LANケーブル作って遊んだり、スイッチいじったり、サーバ設定したりしています。
その中で、Juniperの機器を触り、junosの検証を行う機会があったので、そのときに知った仮想のQFX10kで遊んでみた話をしたいと思います。

カレンダーのURLはこちら
[DMM.com #1 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dmm)
[DMM.com #2 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dmm2)

# はじめに
QFX10kとは、Juniper社のネットワークスイッチの[QFXシリーズ](https://www.juniper.net/jp/jp/products-services/switching/qfx-series/)の一つとなります。
Juniper社では、QFX10kの仮想マシンをサンプルやデモ用にということで公式がgithubにvagrant環境のリポジトリを用意してくれています。
導入方法についても公式で説明されています。
[Juniper/vqfx10k-vagrant](https://github.com/Juniper/vqfx10k-vagrant)

ただし、一部ハマりどころとかがあったのでその辺補足しつつ話していきます。

# 環境
macOS Sierra 10.12.6
Python 2.7.10(system)
brew及びbrew cask導入済み

```shell-session
% /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" # brew導入
% brew tap caskroom/cask # brew cask導入
```


# 導入
基本的にvagrant+virtualboxで動きます。
それに加えてansibleでプロビジョニングを行っているのでそのあたりの環境構築が必要です。

## virtualbox導入
[Juniperのリポジトリ](https://github.com/Juniper/vqfx10k-vagrant/blob/master/INSTALL.md)の手順では、DLしてインストールしろって感じです。
私はbrew caskで導入していたのでその方法について記載します。

```shell-session
% brew cask intall virtualbox
```

## vagrant導入
公式では、こちらもDLしてインストールみたいな形になってますが、同様にbrewでいれてしまいます。

```shell-session
% brew cask install vagrant
```

## Ansible導入
同様にbrewで。
また、optionalになってますが、プロビジョニングされたqfx環境にsshする場合はこちらも必要となるはずです。

```shell-session
% brew install ansible
```

## AnsibleのJuniperパッケージ
Ansibleでjunosに触れる場合は下記も必要となる。

```shell-session
% sudo easy_install pip
% sudo ansible-galaxy install Juniper.junos
% sudo pip install junos-eznc
```

このとき注意が必要でpyenvですでに別環境のpythonを入れている人は、systemのpythonで上記を入れないと動かない可能性があります。
そのため、一度systemのpythonにしてやってからインストールしましょう。

```shell-session
% pyenv global system # 一度systemのpythonに切り替える
% sudo easy_install pip
% sudo ansible-galaxy install Juniper.junos
% sudo pip install junos-eznc
```

# virtual QFX

下記でQFX環境を起動できます
vagrant upすることでansibleとかが動いてプロビジョニングされるはずです

```shell-session
% git clone https://github.com/Juniper/vqfx10k-vagrant.git
% cd vqfx10k-vagrant/full-2qfx
% vagrant up
```

実際に操作してみましょう

```
% vagrant ssh-config > ssh_config
% ssh vqfx1 -F ssh_config
Last login: Mon Dec  4 15:29:43 2017 from 10.0.2.2
--- JUNOS 15.1X53-D63.9 built 2017-04-01 20:45:26 UTC
{master:0}
vagrant@vqfx1> configure
Entering configuration mode

{master:0}[edit]
vagrant@vqfx1# set interfaces xe-0/0/
                                     ^
'xe-0/0/' is ambiguous.
Possible completions:
  <interface-name>     Interface name
  xe-0/0/0             Interface name
  xe-0/0/1             Interface name
{master:0}[edit]
vagrant@vqfx1# set interfaces xe-0/0/3 description test_interface

{master:0}[edit]
vagrant@vqfx1# show | compare
[edit interfaces]
+   xe-0/0/3 {
+       description test_interface;
+   }

{master:0}[edit]
vagrant@vqfx1# commit check
configuration check succeeds

{master:0}[edit]
vagrant@vqfx1# commit
configuration check succeeds
commit complete

{master:0}[edit]
vagrant@vqfx1# exit
Exiting configuration mode

{master:0}
vagrant@vqfx1> show configuration interfaces xe-0/0/3
description test_interface;

{master:0}
vagrant@vqfx1> exit
```


今回使用している[full-2qfx](https://github.com/Juniper/vqfx10k-vagrant/tree/master/full-2qfx)では、リンクにもあるように2台のqfxが起動し、下記のように接続しています

```
    em0|                        em0|
=============  xe-0/0/[0-5] =============
|           | ------------- |           |
|   vqfx1   | ------------- |   vqfx2   |
|           | ------------- |           |
=============               =============
    em1|                        em1|
=============               =============
| vqfx1-pfe |               | vqfx1-pfe |
=============               =============
```

## LAG設定

xe-0/0/0とxe-0/0/1についてはすでに初期設定で使われていますが、xe-0/0/[2-5]については空いているので好きに使えます。

試しにLAGの設定を入れてみましょう。

```
{master:0}[edit]
vagrant@vqfx1# show | compare
[edit]
+  chassis {
+      aggregated-devices {
+          ethernet {
+              device-count 5;
+          }
+      }
+  }
[edit interfaces xe-0/0/3]
+    ether-options {
+        802.3ad ae10;
+    }
[edit interfaces]
+   xe-0/0/4 {
+       ether-options {
+           802.3ad ae10;
+       }
+   }
+   ae0 {
+       unit 0 {
+           family ethernet-switching {
+               interface-mode trunk;
+               vlan {
+                   members vlan10;
+               }
+           }
+       }
+   }
+   ae10 {
+       aggregated-ether-options {
+           lacp {
+               active;
+               periodic fast;
+           }
+       }
+   }
[edit vlans]
+   vlan10 {
+       vlan-id 10;
+   }
```

まあ、VMだからなのか私が設定方法を忘れているのか両側に設定をいれてもこんな感じで何もでないんですけどね。

```
{master:0}
vagrant@vqfx2> show lacp interfaces

{master:0}
vagrant@vqfx2> show lacp interfaces ae10

{master:0}
vagrant@vqfx2>
```

# AnsibleでQFXに触れる

では、とうとうAnsible使ってQFXに触れてみましょう

playbookについては、こちらに上げておきました　
https://github.com/sinnershiki/ansible-vqfx-test

別ディレクトリにてgit cloneしてきてください

```
% git clone https://github.com/sinnershiki/ansible-vqfx-test
% cd ansible-vqfx-test
% cp /path/to/full-2qfx/ssh_config .
% ansible-playbook -i hosts show_config.yml                                                                                                                                                  (git)-[master]

PLAY [show_config] *****************************************************************************************************************************************************************************************

TASK [show_config : show version] **************************************************************************************************************************************************************************
ok: [vqfx2]
ok: [vqfx1]

TASK [show_config : debug show version] ********************************************************************************************************************************************************************
ok: [vqfx1] => {
    "version_result.stdout_lines": [
        [
            "fpc0:",
            "--------------------------------------------------------------------------",
            "Hostname: vqfx1",
            "Model: vqfx-10000",
            "Junos: 15.1X53-D63.9",
            "JUNOS Base OS boot [15.1X53-D63.9]",
            "JUNOS Base OS Software Suite [15.1X53-D63.9]",
            "JUNOS Online Documentation [15.1X53-D63.9]",
            "JUNOS Crypto Software Suite [15.1X53-D63.9]",
            "JUNOS Packet Forwarding Engine Support (qfx-10-f) [15.1X53-D63.9]",
            "JUNOS Kernel Software Suite [15.1X53-D63.9]",
            "JUNOS Web Management [15.1X53-D63.9]",
            "JUNOS Enterprise Software Suite [15.1X53-D63.9]",
            "JUNOS SDN Software Suite [15.1X53-D63.9]",
            "JUNOS Routing Software Suite [15.1X53-D63.9]",
            "JUNOS py-base-i386 [15.1X53-D63.9]"
        ]
    ]
}
ok: [vqfx2] => {
    "version_result.stdout_lines": [
        [
            "fpc0:",
            "--------------------------------------------------------------------------",
            "Hostname: vqfx2",
            "Model: vqfx-10000",
            "Junos: 15.1X53-D63.9",
            "JUNOS Base OS boot [15.1X53-D63.9]",
            "JUNOS Base OS Software Suite [15.1X53-D63.9]",
            "JUNOS Online Documentation [15.1X53-D63.9]",
            "JUNOS Crypto Software Suite [15.1X53-D63.9]",
            "JUNOS Packet Forwarding Engine Support (qfx-10-f) [15.1X53-D63.9]",
            "JUNOS Kernel Software Suite [15.1X53-D63.9]",
            "JUNOS Web Management [15.1X53-D63.9]",
            "JUNOS Enterprise Software Suite [15.1X53-D63.9]",
            "JUNOS SDN Software Suite [15.1X53-D63.9]",
            "JUNOS Routing Software Suite [15.1X53-D63.9]",
            "JUNOS py-base-i386 [15.1X53-D63.9]"
        ]
    ]
}

TASK [show_config : show configuration | display set] ******************************************************************************************************************************************************
ok: [vqfx1]
ok: [vqfx2]

TASK [show_config : debug show configuration] **************************************************************************************************************************************************************
ok: [vqfx1] => {
    "configuration_result.stdout_lines": [
        [
            "set version 15.1X53-D63.9",
            "set system host-name vqfx1",
            "set system root-authentication encrypted-password \"$1$fv3Ke4LT$10nlsy3SEJy5ainm.kPTd.\"",
            "set system root-authentication ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\"",
            "set system login user vagrant uid 2000",
            "set system login user vagrant class super-user",
            "set system login user vagrant authentication ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\"",
            "set system services ssh root-login allow",
            "set system services netconf ssh",
            "set system syslog user * any emergency",
            "set system syslog file messages any notice",
            "set system syslog file messages authorization info",
            "set system syslog file interactive-commands interactive-commands any",
            "set system extensions providers juniper license-type juniper deployment-scope commercial",
            "set system extensions providers chef license-type juniper deployment-scope commercial",
            "set chassis aggregated-devices ethernet device-count 5",
            "set interfaces xe-0/0/0 unit 0 family inet address 10.10.0.1/24",
            "set interfaces xe-0/0/1 unit 0 family inet address 10.10.1.1/24",
            "set interfaces xe-0/0/3 description test_interface",
            "set interfaces xe-0/0/3 ether-options 802.3ad ae10",
            "set interfaces xe-0/0/4 ether-options 802.3ad ae10",
            "set interfaces ae0 unit 0 family ethernet-switching interface-mode trunk",
            "set interfaces ae0 unit 0 family ethernet-switching vlan members vlan10",
            "set interfaces ae10 aggregated-ether-options lacp active",
            "set interfaces ae10 aggregated-ether-options lacp periodic fast",
            "set interfaces em0 unit 0 family inet dhcp",
            "set interfaces em1 unit 0 family inet address 169.254.0.2/24",
            "set interfaces em5 unit 0 family inet address 10.10.5.1/24",
            "set protocols lldp interface all",
            "set vlans default vlan-id 1",
            "set vlans vlan10 vlan-id 10"
        ]
    ]
}
ok: [vqfx2] => {
    "configuration_result.stdout_lines": [
        [
            "set version 15.1X53-D63.9",
            "set system host-name vqfx2",
            "set system root-authentication encrypted-password \"$1$fv3Ke4LT$10nlsy3SEJy5ainm.kPTd.\"",
            "set system root-authentication ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\"",
            "set system login user vagrant uid 2000",
            "set system login user vagrant class super-user",
            "set system login user vagrant authentication ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\"",
            "set system services ssh root-login allow",
            "set system services netconf ssh",
            "set system syslog user * any emergency",
            "set system syslog file messages any notice",
            "set system syslog file messages authorization info",
            "set system syslog file interactive-commands interactive-commands any",
            "set system extensions providers juniper license-type juniper deployment-scope commercial",
            "set system extensions providers chef license-type juniper deployment-scope commercial",
            "set chassis aggregated-devices ethernet device-count 5",
            "set interfaces xe-0/0/0 unit 0 family inet address 10.10.0.2/24",
            "set interfaces xe-0/0/1 unit 0 family inet address 10.10.1.2/24",
            "set interfaces xe-0/0/3 description ae10",
            "set interfaces xe-0/0/3 ether-options 802.3ad ae10",
            "set interfaces xe-0/0/4 description ae10",
            "set interfaces xe-0/0/4 ether-options 802.3ad ae10",
            "set interfaces ae0 unit 0 family ethernet-switching interface-mode trunk",
            "set interfaces ae0 unit 0 family ethernet-switching vlan members vlan10",
            "set interfaces ae10 description ae10",
            "set interfaces ae10 aggregated-ether-options lacp active",
            "set interfaces ae10 aggregated-ether-options lacp periodic fast",
            "set interfaces em0 unit 0 family inet dhcp",
            "set interfaces em1 unit 0 family inet address 169.254.0.2/24",
            "set interfaces em5 unit 0 family inet address 10.10.5.2/24",
            "set protocols lldp interface all",
            "set vlans default vlan-id 1",
            "set vlans vlan10 vlan-id 10"
        ]
    ]
}

PLAY RECAP *************************************************************************************************************************************************************************************************
vqfx1                      : ok=4    changed=0    unreachable=0    failed=0
vqfx2                      : ok=4    changed=0    unreachable=0    failed=0
```

こんな形で設定一覧を引っ張ってこれました。先程のLAGの設定も入っていますね。

現状、vqfxでは、情報を拾ってくるぐらいしかできません。
（vlanは設定できましたが2度めを流し込もうとするとエラーになる。LAGについてはvqfx2だけ設定できない。）

しかし、こうやってAnsibleを流し込む環境を作るためのテストには使えることがわかりました。

# Junosモジュールについて

最後に、実機検証環境でAnsibleを流し込んだ際の一部のモジュールの所感について少しだけお話して終わろうと思います。

## junos_command
公式説明: Run arbitrary commands on an Juniper JUNOS device

任意のコマンドを実行するとのことですが、基本的にはオペレーションモードで設定を見たりするときに使うコマンドのことになります。

実際に使う場合は下記のようなtaskを作り、結果をregisterで保持することになると思います。

```yaml
- name: show version
  junos_command:
    commands: show version
  register: version_result

- name: debug show version
  debug:
    var: version_result.stdout_lines

- name: show configuration | display set
  junos_command:
    commands: show configuration
    display: set
  register: configuration_result

- name: debug show configuration
  debug:
    var: configuration_result.stdout_lines
```

## junos_config
こちらは、configureモードで実際に設定変更を行うモジュールです

下記のように通常通り設定するのと遜色ない形で記述が可能

また、commit時のコメントも設定することができる。ただし、providerにnetconf用のパラメータを渡してやる必要があるのでその点に注意。

```
- name: load configure lines into device
  junos_config:
    lines:
      - set interfaces ge-0/0/8 unit 0 description "Test interface"
      - set vlans vlan100 description "Test vlan"
      - set vlans vlan100 vlan-id 100
      - set interfaces ge-0/0/8 unit 0 family ethernet-switching interface-mode trunk
      - set interfaces ge-0/0/8 unit 0 family ethernet-switching vlan members vlan100
    comment: "ansible update"
    provider: "{{ netconf }}"
  tags:
    - junos_config

```


## junos_interface
文字通りinterfaceについて設定を行うときのモジュール

junos_configとの違いとして、interfaceについて設定をしているというのがひと目で分かる点にあると思います。

また、ただsetを流すだけのjunos_configに比べて汎用的であるため、playbookとして使い回しを考えるならばこういったrolesを作っていくことも視野にいれるのがよいかもしれないですね。

ただし、欠点としては既存の書き方とは大きくことなるため、レビューしづらくなる可能性がありそうです。

```
- name: Create interface using aggregate ae20
  junos_interface:
    aggregate:
      - name: ge-0/0/5
        description: "test_ae20_{{ae20_opposing_device}}_ge-0/0/5"
      - name: ge-0/0/7
        description: "test_ae20_{{ae20_opposing_device}}_ge-0/0/7"

```

これはあくまでインタフェースの設定なので、上記のアグリゲート設定を行ってもLAGが組めるわけではない点に注意が必要です。

## junos_linkagg
LAGを組むためのモジュールです。

こちらはまだ追加されたばかりのモジュールのため、正しい動作をするかどうかの保証はできません。

実際に下記の設定を流し込もうとしたときは、公式example通りにlacpパラメータを設定しようとしたときにエラーが出て失敗しました。

そもそも公式のパラメータ設定にはlacpなんていうパラメータは存在しないので、今後に期待です。

```
- name: configure link aggregation ae20
  junos_linkagg:
    name: ae20
    members:
      - ge-0/0/5
      - ge-0/0/7
    device_count: 5
    state: present
    active: True
#    lacp: active
# lacpは、ansible version2.4ではパラメータがおかしいと言われて動かない可能性がある
```

# まとめ

正直、まだまだ自動化するには障壁の多い世界なのかなと感じることもありました。

ただし、それでもかなり設定をいじることができます。

今後、更に仮想QFXとAnsibleのJuniperモジュールがよくなっていくことで、スイッチもplaybookを使いまわしたりできるとよいですね。

明日は、@teru855さんの記事になります。