Cisco Meeting ServerをAnsibleを使って設定してみる

# 始めに
この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2018 の 17 日目として投稿しています。

2017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco

# 動機
社内の検証でCisco Meeting Server(CMS)をインストールすることがたまにあるんですが、CMSは初期設定が結構面倒で、一番簡単なStand Alone 構成でも結構な手間になります。

ちょっと前までは簡単なShell Scriptを書いていたのですが、この記事ではAnsibleを使ってCMSを設定してみたいと思います。

AnsibleはDevOps界隈ではデファクトスタンダードな構成管理ツールです。ここ最近で、LinuxやWindows以外にも、IOSやNXOSなどのネットワーク機器のサポートも充実してきました。ただ、Ciscoのコラボレーション製品などのサポートは一切なく、これからもなさそうです...

ではCMSの設定をAnsibleではできないのかというと、そんなこともなく、Ansibleのモジュールであるrawを使ってのSSHでのコマンド入力や、uriを使ってのRESTful APIでの設定は可能なので、ここではCMSをこれらのモジュールを使って設定していきます。

冪等性やPlaybookの見た目を考えるとCMS用のモジュールを作成した方がよりAnsibleっぽくなるんですが、あまり時間もなかったので今後の宿題ということで。

# 事前設定
本当はOVAからのVM Deploy、ライセンスの投入、IPアドレスの設定、サービスの設定などをすべてAnsibleで自動化したのですが、VM Deploy、ライセンス投入は社内環境にかなり依存しているので、ここではVM Deploy ~ IPアドレスの設定までは事前に設定してあるものとします。

また、CMSのクラスタリングに関してもサポートしておらず、Stand Aloneのみの構成だけ実施します。


# Playbookの作成
CMSの設定は主にコンソールか、SSH、RESTful APIを使って実施します。
Ansibleのモジュールでは以下を使います。

- raw ... SSH経由でコマンドを発行するモジュール
- uri ... HTTPリクエストを実行するモジュール。RESTful APIなどを実行するのに利用できます

証明書に関しては検証用なので、自己証明書をCMS内で作成しています。より本格的にやるならAnsibleのopenssl系のモジュールを使うとよいかと思います。

```yaml
# Cert
- name: Generate self-signed cert
  raw: pki selfsigned allinone
```

各サービスでやっていることはほとんど同じで、rawモジュールを使って、証明書の設定、Listen Portの設定、サービスの起動をやっている感じです。

```yaml
- name: Set certs for webadmin
  raw: webadmin certs allinone.key allinone.crt
- name: Listen ports for webadmin
  raw: webadmin listen {{ webadmin_interfaces | join(" ") }} {{ webadmin_port }}
- name: Enable Webadmin
  raw: webadmin enable
  register: res
  failed_when: res.stdout.find("FAILURE") > -1
```

Web Bridge, XMPP, TURNの設定時にRESTful APIで設定する必要があることから、uriモジュールを使っています。

```yaml
- name: Add CallBridges to XMPP Service
  uri:
    url: https://{{ ansible_host }}:{{ webadmin_port }}/api/v1/system/configuration/xmpp
    method: PUT
    force_basic_auth: yes
    user: "{{ ansible_user }}"
    password: "{{ ansible_password }}"
    validate_certs: no
    body_format: form-urlencoded
    body:
      uniqueName: "cb_{{ inventory_hostname}}"
      domain: "{{ xmpp_domain }}"
      sharedSecret: "{{ xmpp_callbridge.secret }}"
  delegate_to: localhost
```

XMPPサービスの設定でちょっと面倒なところがあって、コマンドラインで`xmpp callbridge add`すると、出力結果に生成された秘密鍵がでてきますが、それをRESTful APIのパラメータに設定する必要があります。

そのため、`xmpp callbridge list`から秘密鍵の情報を取ってきてAnsibleで変数として補完しておく必要がありました。今回は1つだけ取ってこれば良かったので、regexp系のAnsibleフィルタでごにょごにょやってもいいんですが、将来的に複数のCallBridgeを設定したりするときのために、parse_cli_textfsmというテキストでの出力結果をTextFSMというエンジンでパースしてくれるフィルタを使って秘密鍵などの情報を取ってきています。

```yaml
- name: Get secrets for xmpp
  raw: xmpp callbridge list
  register: cb_res
- name: parse output of xmpp callbridge list
  set_fact:
    xmpp_callbridge: "{{ cb_res.stdout | parse_cli_textfsm(role_path + '/files/xmpp_callbridge_list.fsm') }}"
```

# インベントリ
インベントリファイルでの設定は対象にするホストの設定以外は特にありません。いくつか設定を変数化しているので変更したい場合は、roles/deploy_cms/defaults/main.ymlを参照してください。

```
# CMSのホストを設定。DNS解決できないときはansible_hostでIPアドレスを設定
cms1 ansible_host=192.168.1.1
```

# 実行してみよう
AnsibleからCMSに接続する際の接続方法ですが、SSHの接続以外に、RESTful APIの認証にansible_user, ansible_passwordを使っているので、インベントリ変数に書いておくか、ansible-playbookコマンドで設定してください。

コマンドラインで渡すときは、`-u`でユーザを渡すとPlaybook実行時にansible_userは設定されないようなので、`-e`で変数として渡すようにしてください。

```sh
ansible-playbook -i hosts playbook.yml -k -e ansible_user=admin
```

実行してみた感じ。

```
> ansible-playbook -i hosts playbook.yml -e ansible_user=admin -k
SSH password:

PLAY [Deploy Stand alone CMS] ******************************************************************************************************************

TASK [deploy_cms : Generate self-signed cert] **************************************************************************************************
changed: [cms1]

TASK [deploy_cms : Disble webadmin] ************************************************************************************************************
changed: [cms1]

# snip

TASK [deploy_cms : Add TURN Servers to CallBridge] *********************************************************************************************
ok: [cms1 -> localhost]

PLAY RECAP *************************************************************************************************************************************
cms1                       : ok=47   changed=33   unreachable=0    failed=0
```

Playbookは以下からダウンロードできますよ~。

<https://github.com/oigarash/ansible_cisco_cms>