FUJITSU Cloud Service for OSS (旧K5)をAnsibleで操作する

#はじめに
FUJITSU Cloud Service for OSS (旧K5、表記の便宜上、以降K5と記載します)の第3リージョンでAnsibleが使えるというお話を聞いたので試してみました。
openstackで構築されているため、openstackのモジュールがそのまま利用できます。
まっさらの状態から、ネットワークの設定、インスタンスを起動し、SSHで接続ができるところまでを記録しています。

※本項目の内容には、不具合情報など、現在進行形で流動的な内容が多く含まれるため、できるだけ更新は拾いたいと思いますが、賞味期限は短い可能性があります。

追記:
[第二回](https://qiita.com/dokuyama/items/3f2b887ee425936dea23)の記事も投稿しました。

追記2:
ansible-2.6.4 + openstacksdk 2.2.0 の環境でos_routerの不具合は、エラーは出なくなりましたが、まだ正しくは動作しないようです。

#事前準備
## 第3リージョンの利用開始
K5のポータルにログイン後、IaaS管理を選択し、左側のメニューのリージョンから、jp-east-3, jp-west-3 の利用開始ボタンを押します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/283646/0873bde4-71f7-cfca-6c4c-657de32def8d.png"　width=50%>

## Ansibleで利用するK5の各種ID等の情報収集
必要な情報は以下です。
- auth_url → https://identity.リージョン.cloud.global.fujitsu.com/v3
- username → ポータルへログインする際のユーザー名
- password → ポータルへログインする際のパスワード
- project_domain_name → ポータルへログインする際の契約番号
- user_domain_name → project_domain_nameと同じ
- project_name → IaaS管理のプロジェクトメニューで確認できるプロジェクト名
- project_id → IaaS管理のプロジェクトメニューで確認できるプロジェクトID
- region_name → jp-east-3 または jp-west-3

※ここの情報の分類及び説明は、筆者が正確に理解していないため不正確な可能性があります

#Ansibleの実行
## ansibleの準備
手元のCentOS6のrpm導入済みのpython3.x(epel)ではうまく動作しなかったため、pyenvを利用して環境を構築します。
※[Pyenvの使い方](https://qiita.com/mogom625/items/b1b673f530a05ec6b423)を参照させて頂きました。

**pipでインストールするAnsible、shadeのバージョンを指定していますが、2018/8/24時点での最新版(2.6.3)では、一部機能がどうしても動作しなかったため、うまく動作する組み合わせを指定しています。**

以下のコマンドで導入しました。

```
$ curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
$ cat << _EOF >> ~/.bashrc
export PATH="\$HOME/.pyenv/bin:\$PATH"
eval "\$(pyenv init -)"
eval "\$(pyenv virtualenv-init -)"
_EOF
$ source ~/.bashrc
$ pyenv install 3.6.6
$ pyenv global 3.6.6
$ pip install ansible==2.5.5
$ pip install shade==1.28.0
$ pip install python-novaclient
```


## ansible のplaybookの用意
以下の2ファイルを用意します。

- **clouds.yml** .... 
利用するアカウントの情報を記載します。上の項目で用意した情報を記載してください。

```
clouds:
 jp-east-3:
  auth:
   auth_url: https://identity.jp-east-3.cloud.global.fujitsu.com/v3
   username: ****
   password: ****
   project_domain_name: ****
   user_domain_name: ****
   project_name: ****
   project_id: ****
  identity_api_version: '3'
  region_name: ****
```

- **site.yml** .... こちらが処理の本体です。少々長いですが、今回は1ファイルで済ませました。
少々冗長ではあるのですが、複数のルールを登録する場合など、ansibleに慣れていなかった筆者は記法がなかなかわからず困ったので、そのまま残しています。

```
---
- hosts: localhost
  tasks:
    - name: create network
      os_network:
        cloud: jp-east-3
        state: present
        name: demo-net
    - name: create subnet
      os_subnet:
        cloud: jp-east-3
        name: demo-subnet
        network_name: demo-net
        cidr: 10.122.0.0/24
        dns_nameservers: 8.8.8.8
        state: present
    - name: create router
      os_router:
        cloud: jp-east-3
        name: demo-router
        state: present
        network: fip-net
        interfaces:
          - demo-subnet
    - name: create secutiry group mng
      os_security_group:
        cloud: jp-east-3
        state: present
        name: mng
    - name: add secutiry group rule mng from XXX.XXX.XXX.XXX/XX
      os_security_group_rule:
        cloud: jp-east-3
        state: present
        security_group: mng
        protocol: tcp
        port_range_min: 1
        port_range_max: 65535
        remote_ip_prefix: XXX.XXX.XXX.XXX/XX
    - name: add secutiry group rule mng from YYY.YYY.YYY.YYY/YY
      os_security_group_rule:
        cloud: jp-east-3
        state: present
        security_group: mng
        protocol: tcp
        port_range_min: 1
        port_range_max: 65535
        remote_ip_prefix: YYY.YYY.YYY.YYY/YY
    - name: create secutiry group common
      os_security_group:
        cloud: jp-east-3
        state: present
        name: common
      register: security_group_common
    - name: add secutiry group rule common tcp
      os_security_group_rule:
        cloud: jp-east-3
        state: present
        security_group: common
        protocol: tcp
        port_range_min: 1
        port_range_max: 65535
        remote_group: common
    - name: add secutiry group rule common udp
      os_security_group_rule:
        cloud: jp-east-3
        state: present
        security_group: common
        protocol: udp
        port_range_min: 1
        port_range_max: 65535
        remote_group: common
    - name: add secutiry group rule common icmp
      os_security_group_rule:
        cloud: jp-east-3
        state: present
        security_group: common
        protocol: icmp
        remote_group: common
    - name: create keypair
      os_keypair:
        cloud: jp-east-3
        state: present
        name: demo-key
      register: keypair
#    - debug: var=keypair
    - name: Create local public key
      when: keypair.changed
      local_action:
        module: copy
        content: "{{ keypair.key.public_key }}"
        dest: "~/.ssh/jp-east-3-{{ keypair.key.name }}.pub"
    - name: Create local private key
      when: keypair.changed
      local_action:
        module: copy
        content: "{{ keypair.key.private_key }}"
        dest: "~/.ssh/jp-east-3-{{ keypair.key.name }}"
        mode: 0600
      ignore_errors: True
    - name: create instance
      os_server:
        cloud: jp-east-3
        state: present
        name: demo-instance
        image: "CentOS 7.8 64bit (English) 01"
        flavor: P3-1
        key_name: demo-key
        security_groups:
          - mng
          - common
        network: demo-net
        auto_ip: yes
        boot_from_volume: True
        volume_size: 35
      register: server
    - debug: var=server
```

行っている処理は以下の通りです。

- ネットワークの作成
- Subnetの作成
- ルータの作成
- セキュリティグループの作成 (mng, common)
    - mngは外部からの管理アクセスのようなものを想定(例では全ポートだが、SSH等を想定)
    - commonはローカル内での通信の許可
- セキュリティグループへのルールの追加
    - mng向け: XXX.XXX.XXX.XXX/XX、YYY.YYY.YYY.YYY/YY から全TCPポートでのアクセス許可
    - common向け: tcp, udp, icmpの全許可
- sshで使用するkeypairの作成及び変数への登録
- 作成したpublic key, private keyのファイルへの保存
    - ※keypairの作成に成功した場合、~/.ssh/jp-east-3-demo-key, ~/.ssh/jp-east-3-demo-key.pub を上書きするので、念の為注意してください
- インスタンスの作成 (Global IPを割り当てています)
- debugでaccessIPv4が確認できるように表示する

## ansible-playbookの実行
```
$ ansible-playbook site.yml
```

無事成功すれば、accessIPv4 で確認できるIPアドレスに以下で接続できるはず…！

```
$ ssh -i ~/.ssh/jp-east-3-demo-key k5user@確認したIPアドレス
```


#はまった点
- epelで入るpython3.4.x にpip で導入したansibleでは、以下のようなエラーが出て最初のnetworkの作成から成功せずに先に進めませんでした。

```
fatal: [localhost]: FAILED! => {"changed": false, "msg": "NotFoundException:
404: Client Error for url:
https://networking.jp-east-3.cloud.global.fujitsu.com/v2.0/networks.json,
{\"error\": {\"message\": \"The resource could not be found.\", \"code\":
404, \"title\": \"Not Found\"}}"} 
```

- 2018/8/24現在の最新のansibleでは、create routerで、外部接続用networkを指定するとエラーになりました。


```
atal: [localhost]: FAILED! => {"changed": false, "msg": "Error creating router demo-router: Client Error for url: https://networking.jp-east-3.cloud.global.fujitsu.com/v2.0/routers.json, {\"NeutronError\": {\"message\": \"{u'external_gateway_info': {u'network_id': u'XXXXXXXX', u'enable_snat': True}, 'nuage_backhaul_rd': None, 'nuage_backhaul_vnid': None, u'name': u'demo-router', 'tunnel_type': 'DEFAULT', u'admin_state_up': True, 'rt': None, 'tenant_id': u'XXXXXXXX', 'description': '', 'nuage_backhaul_rt': None, 'ecmp_count': None, 'net_partition': None, 'nuage_router_template': None, 'rd': None, 'project_id': u'XXXXXXXX', 'nuage_underlay': None} is disallowed by policy rule (rule:create_router and (rule:create_router:external_gateway_info and (rule:create_router:external_gateway_info:network_id and rule:create_router:external_gateway_info:enable_snat))) with {'project_id': u'XXXXXXXX', 'domain': None, 'project_name': u'XXXXXXXX', 'user_id': u'XXXXXXXX', 'roles': [u'_member_', u'cpf_org_manager', u'cpf_admin'], 'user_domain_id': None, 'service_project_id': None, 'project_domain': None, 'tenant_id': u'XXXXXXXX', 'service_user_domain_id': None, 'service_project_domain_id': None, 'service_roles': [], 'is_admin_project': True, 'service_user_id': None, 'is_admin': False, 'user': u'XXXXXXXX', 'tenant_name': u'XXXXXXXX', 'user_domain': None, 'user_name': u'XXXXXXXX', 'tenant': u'XXXXXXXX', 'project_domain_id': None} \", \"type\": \"PolicyNotAuthorized\", \"detail\": \"\"}}"}
```
本エラーを確認したのは以下の組み合わせ。
- ansible 2.6.3, openstacksdk 0.17.2
- ansible 2.5.5, shade 1.29.0