LoginSignup
6
3

More than 3 years have passed since last update.

AnsibleでNetApp ONTAP Storageを設定

Last updated at Posted at 2020-04-28

2020/10 いくつか最近のバージョンでの注意点を追記しました。記事を書いてから2年以上経っていて内容が古くなっていますので近いうちに2.10版として更新したいと思っています
Ansible 2.10 + NetApp ONTAP Collection 20.10.0 を使った SSLの Client認証使うときの設定について記事を書きましたので、新しい環境の方はこちらも見ていただければと思います

AnsibleでNetApp ONTAP Storageを設定

Ansible 2.6以後、NetAppストレージ用のモジュールが追加されました。

この記事では NetApp ONTAP9を搭載したストレージの Clusterが出来た状態から初めて、
NFSでアクセス可能な領域=ボリュームをクライアントに公開するところまでを行うPlayBookを作っています。

お約束

この記事で書かれている PlayBookや Roleは動作を保証されたものではありません。
各環境に合わせてテスト・確認をしたうえで自己責任にて利用していただければと思います。

Ansibleの環境

前提とする ONTAPクラスタの状態

  • クラスタが組まれている事
  • クラスタ管理IP・ノード管理IPに Ansibleを動かすホストからネットワークが届く事

下準備

ONTAP cluster で HTTPによるアクセスを有効化します

# enable http api access
ssh admin@ONTAP_IP_ADDRESS 'set -privilege advanced; system services web modify -http-enabled true;'

pythonの netapp-libを追加

sudo apt-get install -y python-pip
pip install netapp-lib

Roleと変数とInventory

ONTAPの Ansibleモジュールは かなり細かい粒度(API粒度)で用意されており、
タスク毎に認証情報が必要な事等から普通に書くとかなり冗長な感じになってしまいます。

これを緩和するために一部Roleを使って書いています。

また、認証情報は PlayBookに書かず、変数を利用するようにしていますが
クラスタで一つのファイルにしておきたいので Inventoryを階層的に定義しています。

Role

今回用意したRoleのは以下の2+1個です。

  • VLAN I/Fの作成
  • Broadcastドメインの作成

  • Factを集めて表示する

Roleのメンテナンスが面倒なので、再利用・ループが必要無いようなところは普通にベタっと書いています。

Role:VLAN I/Fの作成

各ノードに対応した host_varsでデータ用インターフェースの一覧を書いておき、
必要に応じて このRoleでvlanインターフェースを作る様にしています。

roles/datavlan/tasks/main.yml
- na_ontap_net_vlan:
    state=present
    vlanid={{ vlan_id }}
    node={{ inventory_hostname }}
    parent_interface={{ item }}
    username={{ cluster_username }}
    password={{ cluster_password }}
    hostname={{ cluster_hostname }}
  loop: "{{ data_interfaces }}"

Role:Broadcastドメインの作成

クラスタ内で同一のvlan idを持っているI/Fを集めてブロードキャストドメインを作ります。
vlanのroleと ノードのデータinterface一覧から パラメータを作っているのでちょっと長いです。

roles/bcdomain/tasks/main.yml
- set_fact:
    vlan_interface_list: >-
      {%- set templist = [] -%}
      {%- for i in hostvars[inventory_hostname]['groups'][inventory_hostname+'-nodes'] -%}
      {%-   if hostvars[i]['vlan_interface_dict'] is defined -%}
      {%-     if hostvars[i]['vlan_interface_dict'][vlan_id] is defined -%}
      {%-       set _ = templist.append(hostvars[i]['vlan_interface_dict'][vlan_id]|join(",")) -%}
      {%-     endif -%}
      {%-   endif -%}
      {%- endfor -%}
      {{ templist }}
- name: create broadcast domain for vlan {{ vlan_id }}
  na_ontap_broadcast_domain:
    state=present
    username={{ cluster_username }}
    password={{ cluster_password }}
    hostname={{ cluster_hostname }}
    broadcast_domain={{ bc_name }}
    mtu=9000
    ipspace={{ ipspace }}
    ports={{ vlan_interface_list|join(",")}}

Role:Factの収集

2020/10 2.9時点で na_ontap_gather_factsは Deprecatedになっています。2.13で削除される予定です。_info 系の利用が推奨されます

Ansibleから Storageを管理するためには、代理となるサーバからStorage上にAPIを発行します。
そのままでは Storageの Factを集める事が出来ませんので、ここもモジュールで対応する事になります。

簡単なので普通に書いても良いのですが、いつも使うので Roleにしておきました。

roles/ontap_facts/tasks/main.yml
- name: Get ONTAP Info
  na_ontap_gather_facts:
    state: info
    username: "{{ cluster_username }}"
    password: "{{ cluster_password }}"
    hostname: "{{ cluster_hostname }}"
- debug: var=ontap_facts

変数

クラスタの認証情報と接続先をグループ変数で設定します。

なお、グループ/ホスト変数双方とも ansible_hostに local_hostを指定しています。
playbookを実行するホスト自身に netapp-lib(APIを叩くためのライブラリ)が
インストールされている必要があります。
ストレージの操作を他のホストで実行したい場合は ansible_hostを適宜変更する必要があります。

グループ変数

クラスタ毎の認証情報を含めて作成します。ansible-vaultで暗号化すると良いかと思います。

group_vars/クラスタ名-cluster.yml
---
ansible_host: localhost
cluster_name:     "クラスタ名"
cluster_username: "admin"
cluster_password: "パスワード"
cluster_hostname: "IPアドレス/ホスト名"

2020/10 最近のVersionで ansible_host側での Python3が必要な場合があるようです。pip3 install netapp-lib 及び 下記の様にInterpreterの指定の追加が必要な場合がありました。

ansible_python_interpreter: "/usr/bin/python3"

ホスト変数

ノード毎の情報です。

host_vars/ノード名.yml
---
node_hostname: "<ノード管理IPアドレス>"
data_interfaces:
  - e0e
  - e0g

data_interfacesにデータインターフェースとして利用する予定のインターフェース名を列挙しています。
これは VLAN I/F作成Roleとブロードキャストドメイン作成Roleで利用していますので
それらのRoleが不要であれば特に設定する必要はありません。

Inventory

実際にネットワーク上に存在するのは以下ですが、これを階層的にします。

  • AFF クラスタ
  • AFF-01 ノード
  • AFF-02 ノード
  • svm01 ストレージ仮想マシン
  • svm02 ストレージ仮想マシン...

グループ階層はこんな形になります。

AFF-cluster グループ

  • AFF
  • AFF-nodes グループ
    • AFF-01
    • AFF-02...
  • AFF-svms グループ
    • svm01
    • svm02...

インベントリファイルは以下のようにしています。

ノード名とクラスタ名は実機の設定に完全一致させる必要がありますので
実機の設定通りに大文字小文字等を変えないようにしてください。

lab_aff.yml
---
#
# AFF-clusterグループ
#
AFF-cluster:
  hosts:
    AFF:
  children:
    AFF-nodes:
    AFF-svms:
#
# AFFクラスタのノード一覧
#
AFF-nodes:
  hosts:
    AFF-01:
    AFF-02:
#
# AFFクラスタのSVM一覧
#
AFF-svms:
  hosts:
    svm01:
    svm02:

タスク・ロールの設定

実際に構成タスクを書いていきます。

各タスクでどこを対象に(hosts指定)しているかを気にしながら見ていただければと思います。

Factの収集

「クラスタに対して」Roleを使ってFactを集めてDebugで表示させます。

#
# ONTAPクラスタのFactsを集める
#
- hosts: AFF
  gather_facts: no
  roles:
    - { role: ontap_facts }

Licenseの登録

「クラスタに対して」ライセンスを登録します。

ロール定義してループで回してやるべきかなと思いつつ今はベタっと書いてます。
以下はbaseだけですが、同様にnfs/iscsi/fcp等も登録します。

#
# ライセンスの登録
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: base license
      na_ontap_license:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        license_codes: XXXXXXXXXXXXXXAAAAAAAAAAAAAA
        license_names: base

Aggregateの作成

「クラスタに対して」各ノードでAggregateを作ります。

指定はディスク本数とディスクタイプ、名前を指定します。

#
# ディスクカウントとタイプ、名前を指定してAggregateを作ります。
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: create aggregate aggr01
      na_ontap_aggregate:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        disk_count: 22
        disk_type: SSD
        name: aggr01
        nodes: AFF-01

    - name: create aggregate aggr02
      na_ontap_aggregate:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        disk_count: 22
        disk_type: SSD
        name: aggr02
        nodes: AFF-02

Interface Groupの作成

「クラスタ内の全ノードに対して」LACP等で物理NICのチームを作ります。

各ノードに対してすべて同じ設定をしています。

以下の例では e0cとe0d を Default bcdomainから抜いたあとに、
ifgrp a0aをlacpで作成して 各ポートを所属させます。

注意:一度インターフェースを作成したあとに、この設定ファイルのmode等を変更して
再度PlayBookを走らせてもifgrpの設定は変更されません。
一度明示的にifgrpを消したあとに実施してください。

#
# インターフェースグループの作成
#
- hosts: AFF-nodes
  gather_facts: no
  tasks:
    - name: remove ports from default bcdomain.
      na_ontap_broadcast_domain_ports:
        state: absent
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        broadcast_domain: Default
        ports: "{{ inventory_hostname }}:e0c"

    - name: remove ports from default bcdomain.
      na_ontap_broadcast_domain_ports:
        state: absent
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        broadcast_domain: Default
        ports: "{{ inventory_hostname }}:e0d"

    - name: create or add port to ifgrp for e0c
      na_ontap_net_ifgrp:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        mode: multimode_lacp
        distribution_function: port
        node: "{{ inventory_hostname }}"
        name: a0a
        port: e0c

    - name: add port to ifgrp for e0d
      na_ontap_net_ifgrp:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        mode: multimode_lacp
        distribution_function: port
        node: "{{ inventory_hostname }}"
        name: a0a
        port: e0d

VLAN I/Fを作成

「クラスタ内の全ノードに対して」データインターフェース上にVLAN I/Fを作ります。

データインターフェースと書いていますが、
host_vars内で設定したインターフェースの事です。

#
# vlan i/fの作成
#
- hosts: AFF-nodes
  gather_facts: no
  roles:
    - { role: datavlan, vlan_id: 1011 }
    - { role: datavlan, vlan_id: 1012 }

Broadcast Domainの作成

「クラスタに対して」VLAN I/Fに対応した ブロードキャスト ドメインを作ります。

- hosts: AFF
  gather_facts: no
  roles:
    - { role: bcdomain, vlan_id: 1011, ipspace: 'Default' }
    - { role: bcdomain, vlan_id: 1012, ipspace: 'Default' }

ipspaceの作成はAnsible 2.7から サポートされています。

SVMを作る

「クラスタに対して」SVMを作成します。


#
# create a svm.
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: create svm
      na_ontap_svm:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        name: svm01
        aggr_list: aggr01,aggr02
        root_volume: volroot
        root_volume_aggregate: aggr01
        root_volume_security_style: unix
    - name: create svm
      na_ontap_svm:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        name: svm02
        aggr_list: aggr01,aggr02
        root_volume: volroot
        root_volume_aggregate: aggr02
        root_volume_security_style: unix

LIFを作る

「クラスタに対して」SVMを指定してLIFを作成します。

#
# create lif for node a300-01
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: create svm lif.
      na_ontap_interface:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        vserver: svm01
        interface_name: lif_svm01
        home_node: AFF-01
        home_port: a0a
        role: data
        protocols: nfs,cifs
        address: xxx.xxx.xxx.xxx
        netmask: 255.255.255.0

Export Policyを作る

「クラスタに対して」SVMを指定してExport Policyを作成します。

#
# create export policy.
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: create export policy.
      na_ontap_export_policy_rule:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        vserver: svm01
        client_match: 0.0.0.0/0
        policy_name: default
        protocol: any
        ro_rule: any
        rw_rule: any
        super_user_security: any

NFSサービスをEnableにする

「クラスタに対して」SVMを指定してNFSサービスをEnableにします。

#
# enable nfs service.
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: enable nfs service.
      na_ontap_nfs:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        vserver: svm01
        service_state: started
        nfsv3: enabled
        nfsv4: enabled
        nfsv41: enabled

ボリュームを作る

「クラスタに対して」SVMを指定してボリュームを作成します。

#
# make volume.
#
- hosts: AFF
  gather_facts: no
  tasks:
    - name: make volume.
      na_ontap_volume:
        state: present
        hostname: "{{ cluster_hostname }}"
        username: "{{ cluster_username }}"
        password: "{{ cluster_password }}"
        vserver: svm01
        name: vol_data1
        size: 200
        size_unit: gb
        space_guarantee: none
        aggregate_name: aggr01
        volume_security_style: unix
        type: rw
        is_online: yes
        junction_path: /data1

まとめ

ここまでで ONTAPの初期構築に必要な最低限の作業として

  • aggregate
  • network
  • svm
  • lif
  • service
  • volume

が作成出来るかと思います。便利ですので是非試してみてもらえればと思います。

改善すべき点

以下は改善すべき点かと思ってます。

  • 安全のため svm内で完結するオペレーションは svmスコープで作業すべき(今はCluster Scope)
  • ライセンス登録なんかは繰り返し書くのはダルいのでRole化
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3