はじめに
ONTAP 9.9.1以降のVersionではSystem Managerと呼ばれるGUI管理ツールでVolumeやLUN作成のようなプロビジョニング操作をAnsible Playbookとして生成する事ができます。
生成されるPlaybook用には、対話型や別ファイルで定義される変数がありますので、前回の記事を参照する事で、より理解が進むかと思います。
この記事では、生成されたPlaybookの一部を修正し、実機での確認と実行を試みる内容になっています。
System ManagerではCLIとは異なり、PolicyやServiceレベル指定で設定を行います。
(CLIのような細かい指定を個々に実施しないで設定)
何をしたい?できる?
- System ManagerでAnsible Playbookを作成する
- 生成されたPlaybookをSVM向け修正して実行する
ONTAPのSystem Managerについて
System ManagerはGUIを使用して管理されるため、Webブラウザ上からStorageシステムやそのオブジェクト(SVM、Volume、LIF、Aggregateなど)の管理や一般的な管理作業が実行できます。
ただし、GUIへのログインにはクラスタ管理者の認証が必要であるため、各SVMに用意された管理者権限で操作することはできません。(SSHやAPIを使用することはできます。)
そのため、生成されたPlaybookにはクラスタ管理ようのIPの記載がなされる事になり、環境によっては各SVMの管理IPに修正する必要がある場合があります。
生成されたPlaybookはサンプル扱いの為、保証やサポートされるものではありません。(生成されたPlaybookのREADME.txtに記載)
![]() |
---|
ONTAPのStorageサービスのレベル定義について
ONTAPには事前定義されたサービスレベルがあり、System ManagerではこのサービスレベルやQos Policyを指定してVolume作成を実施します。サービスレベルはDiskの種類で異なり、以下のように定義されています。
(このサービスレベルはONTAP上では変更できない)
(以下のIOPS等の内容はAdaptive QoSの内容と同じ)
Storageサービス | 対象Device | IOPS(SLA) | ピークIOPS | 最小IOPS | 想定Latency |
---|---|---|---|---|---|
value | 全デバイス | 128 per TB | 512 per TB | 75 | 17 ms |
performance | AFF | 2048 per TB | 4096 per TB | 500 | 2 ms |
extreme | AFF | 6144 per TB | 12288 per TB | 1000 | 1 ms |
最近のONTAPではSystem Managerでもマニュアル設定でAggregate指定をしてのVolume作成は可能ですが、現時点ではAnsible Playbook生成には反映されないので、この記事では実施しません。
StorageサービスをCLIで確認する際には、Advanceモードでstorage-service showコマンドを実行します。
> set advanced
Warning: These advanced commands are potentially dangerous; use them only when directed to do so by NetApp personnel.
Do you want to continue? {y|n}: y
*> storage-service show -vserver cifs100 -instance
Vserver: cifs100
Storage Service: -CAPACITY-TIER-CUSTOM-STORAGE-SERVICE-
Description: Capacity Tier
Is Internal: true
Expected IOPS per TB: 128
Expected IOPS Allocation: allocated-space
Peak IOPS per TB: 512
Peak IOPS Allocation: used-space
Absolute Minimum IOPS: 75
Target Latency (ms): 17
Aggregate List: aggr1_node1, aggr1_node2
Aggregate Costs: 300, 300
Vserver UUID: ec32fcb7-bc0a-11ed-b26c-00a098f0b86b
UUID: 40b6d132-4255-8e4c-9916-8fb0ce343f09
Vserver: cifs100
Storage Service: extreme
Description: Extreme
Is Internal: false
Expected IOPS per TB: 6144
Expected IOPS Allocation: allocated-space
Peak IOPS per TB: 12288
Peak IOPS Allocation: used-space
Absolute Minimum IOPS: 1000
Target Latency (ms): 1
Aggregate List: aggr1_node1, aggr1_node2
Aggregate Costs: 0, 0
Vserver UUID: ec32fcb7-bc0a-11ed-b26c-00a098f0b86b
UUID: 0743fa34-43b7-4a87-ba8f-96816a0590a0
Vserver: cifs100
Storage Service: maxdata
Description: MAX Data ONTAP-Internal Performance
Is Internal: true
Expected IOPS per TB: 8000
Expected IOPS Allocation: allocated-space
Peak IOPS per TB: 32000
Peak IOPS Allocation: allocated-space
Absolute Minimum IOPS: 8000
Target Latency (ms): 1
Aggregate List: aggr1_node1, aggr1_node2
Aggregate Costs: 0, 0
Vserver UUID: ec32fcb7-bc0a-11ed-b26c-00a098f0b86b
UUID: b4f11909-4323-1ee4-300c-a69e75466f85
Vserver: cifs100
Storage Service: performance
Description: Performance
Is Internal: false
Expected IOPS per TB: 2048
Expected IOPS Allocation: allocated-space
Peak IOPS per TB: 4096
Peak IOPS Allocation: used-space
Absolute Minimum IOPS: 500
Target Latency (ms): 2
Aggregate List: aggr1_node1, aggr1_node2
Aggregate Costs: 200, 200
Vserver UUID: ec32fcb7-bc0a-11ed-b26c-00a098f0b86b
UUID: 0069e671-43bd-be20-dccf-f4983562b9a7
Vserver: cifs100
Storage Service: value
Description: Value
Is Internal: false
Expected IOPS per TB: 128
Expected IOPS Allocation: allocated-space
Peak IOPS per TB: 512
Peak IOPS Allocation: used-space
Absolute Minimum IOPS: 75
Target Latency (ms): 17
Aggregate List: aggr1_node1, aggr1_node2
Aggregate Costs: 300, 300
Vserver UUID: ec32fcb7-bc0a-11ed-b26c-00a098f0b86b
UUID: f424a03e-407b-cfb0-a1a9-e4ac238cb842
5 entries were displayed.
記事における環境情報
本記事では、以下の環境で実施した内容となります。
(Ansible環境は過去の記事と同じ)
- ONTAP(AFF) : 9.10
- Object Storage(Tiering)は用意も設定も無し
操作対象のSVMのAggregate listが空白の場合は、Playbook操作が失敗しますので、SVM新規作成時は注意が必要です。(GUIでは操作できてPlaybookでは失敗する)
環境のイメージとしては以下の通りです。
![]() |
---|
設定手順
この手順例では、Volume作成のPlaybookの生成を実行しています。
1. System Managerへのアクセス
クラスタ管理LIFのIPアドレスをWebブラウザのURLバーに入力し、表示された画面でアカウントを入力します。
|
2. System Manager上でVolume作成画面へ移動し、必要な値を記載
画面左側の[ストレージ]=>[ボリューム]をクリックし、表示された画面の上の方にある[追加]をクリックします。
|
Volume作成に必要な内容を入力します。
[パフォーマンスサービスレベル]の項目で、[カスタム]->[既存]からvalueを選択します。
[パフォーマンスサービスレベル]の項目で、[カスタム]ではなく[バリュー]を選んだ場合は、QoSの最大値のみを設定されているqos policy-groupのvalue-fixedが適用されます。
画面下方の[Ansible Playbookに保存]をクリックすると、Playbookがダウンロードされます。
3. ダウンロードしたPlaybookの確認
Zip形式でダウンロードされたPlaybookを展開して内容を確認してみると、変数とPlaybookが分かれた形であることが確認できます。
中を確認すると、対話型や別ファイルで記載するなどの方法で使用される変数や、
nas_application_templateで利用するtemplateの記載が確認できます。
volumeAdd.yaml
---
- hosts: localhost
vars_prompt:
- name: "username"
prompt: "Enter UserName"
private: no
- name: "password"
prompt: "Enter Password"
private: yes
vars:
login: &login
hostname: "{{ host.ip }}"
username: "{{ username }}"
password: "{{ password }}"
https: true
validate_certs: false
vars_files:
- volumeAdd_variable.yaml
collections:
- netapp.ontap
tasks:
- name: Prompt remote cluster IP
pause:
prompt: Please enter the remote cluster IP for creating Snapmirror relationship
when: UserInputs.snapmirror is defined and (UserInputs.remoteClusterIP|default(None) == None)
register: remoteClusterHostIP
- set_fact:
remoteClusterIP: "{{ UserInputs.remoteClusterIP if (UserInputs.remoteClusterIP|default(None) != None) else remoteClusterHostIP.user_input }}"
when: UserInputs.snapmirror is defined
- set_fact:
maxThroughput: "{{ UserInputs.nas_application_template.qos.policy.fixed.max_throughput_mbps + 'MB/s' if((UserInputs.nas_application_template.qos.policy.fixed.max_throughput_mbps | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.max_throughput_mbps != '0') else ''}}{{',' if ((UserInputs.nas_application_template.qos.policy.fixed.max_throughput_mbps | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.max_throughput_mbps != '0') and ((UserInputs.nas_application_template.qos.policy.fixed.max_throughput_iops | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.max_throughput_iops != '0') else ''}}{{UserInputs.nas_application_template.qos.policy.fixed.max_throughput_iops + 'iops' if((UserInputs.nas_application_template.qos.policy.fixed.max_throughput_iops | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.max_throughput_iops != '0') else '' }}"
minThroughput: "{{ UserInputs.nas_application_template.qos.policy.fixed.min_throughput_mbps + 'MB/s' if((UserInputs.nas_application_template.qos.policy.fixed.min_throughput_mbps | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.min_throughput_mbps != '0') else ''}}{{',' if ((UserInputs.nas_application_template.qos.policy.fixed.min_throughput_mbps | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.min_throughput_mbps != '0') and ((UserInputs.nas_application_template.qos.policy.fixed.min_throughput_iops | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.min_throughput_iops != '0') else ''}}{{UserInputs.nas_application_template.qos.policy.fixed.min_throughput_iops + 'iops' if((UserInputs.nas_application_template.qos.policy.fixed.min_throughput_iops | default('', true) | trim != '') and UserInputs.nas_application_template.qos.policy.fixed.min_throughput_iops != '0') else '' }}"
- name: Create or modify qos policy
when: UserInputs.nas_application_template.qos is defined and UserInputs.nas_application_template.qos.policy is defined and UserInputs.nas_application_template.qos.policy.name is defined and UserInputs.nas_application_template.qos.policy.name != 'none' and UserInputs.nas_application_template.qos.policy.name != 'extreme' and UserInputs.nas_application_template.qos.policy.name != 'extreme-fixed' and UserInputs.nas_application_template.qos.policy.name != 'value' and UserInputs.nas_application_template.qos.policy.name != 'value-fixed' and UserInputs.nas_application_template.qos.policy.name != 'performance' and UserInputs.nas_application_template.qos.policy.name != 'performance-fixed'
na_ontap_qos_policy_group:
state: present
name: "{{ UserInputs.nas_application_template.qos.policy.name }}"
vserver: "{{ UserInputs.svm.name }}"
max_throughput: "{{ omit if( maxThroughput == '') else maxThroughput }}"
min_throughput: "{{ omit if( minThroughput == '') else minThroughput}}"
is_shared: "{{ UserInputs.nas_application_template.qos.policy.fixed.capacity_shared | default(omit)}}"
<<: *login
- name: create FlexVolume or FlexGroup
when: UserInputs.name is defined and UserInputs.nas_application_template.flexcache is not defined
na_ontap_volume:
state: present
name: "{{ UserInputs.name }}"
vserver: "{{ UserInputs.svm.name }}"
size: "{{UserInputs.size}}"
size_unit: "{{ (UserInputs.size_unit | lower) if (UserInputs.size_unit is defined) else 'gb' }}"
auto_provision_as: "{{ UserInputs.auto_provision_as | default(omit) }}"
snapshot_policy: "{{ UserInputs.nas_application_template.snapshot_policy | default(omit) }}"
qos_policy_group: "{{ omit if( UserInputs.nas_application_template.qos.policy.name is not defined or UserInputs.nas_application_template.qos.policy.name == 'extreme' or UserInputs.nas_application_template.qos.policy.name == 'performance' or UserInputs.nas_application_template.qos.policy.name == 'value') else UserInputs.nas_application_template.qos.policy.name }}"
qos_adaptive_policy_group: "{{ omit if( UserInputs.nas_application_template.qos.policy.name is not defined or (UserInputs.nas_application_template.qos.policy.name != 'extreme' and UserInputs.nas_application_template.qos.policy.name != 'performance' and UserInputs.nas_application_template.qos.policy.name != 'value')) else UserInputs.nas_application_template.qos.policy.name }}"
export_policy: "{{ UserInputs.nas_application_template.export_policy.name | default(omit) }}"
nas_application_template:
use_nas_application: true
storage_service: "{{ UserInputs.nas_application_template.storage_service.name | default(omit) }}"
tiering:
control: "{{ UserInputs.nas_application_template.tiering.control | default(omit) }}"
policy: "{{ UserInputs.nas_application_template.tiering.policy | default(omit) }}"
object_stores: "{{ UserInputs.nas_application_template.tiering.object_stores[0] | default(omit) }}"
nfs_access: "{{ UserInputs.nas_application_template.nfs_access | default(omit) }}"
cifs_access: "{{ UserInputs.nas_application_template.cifs_access | default(omit) }}"
<<: *login
- name: create FlexCache
when: UserInputs.name is defined and UserInputs.nas_application_template.flexcache is defined
na_ontap_volume:
state: present
name: "{{ UserInputs.name }}"
vserver: "{{ UserInputs.svm.name }}"
size: "{{UserInputs.size}}"
size_unit: "{{ (UserInputs.size_unit | lower) if (UserInputs.size_unit is defined) else 'gb' }}"
auto_provision_as: "{{ UserInputs.auto_provision_as | default(omit) }}"
snapshot_policy: "{{ UserInputs.nas_application_template.snapshot_policy | default(omit) }}"
qos_policy_group: "{{ omit if( UserInputs.nas_application_template.qos.policy.name is not defined or UserInputs.nas_application_template.qos.policy.name == 'extreme' or UserInputs.nas_application_template.qos.policy.name == 'performance' or UserInputs.nas_application_template.qos.policy.name == 'value') else UserInputs.nas_application_template.qos.policy.name }}"
qos_adaptive_policy_group: "{{ omit if( UserInputs.nas_application_template.qos.policy.name is not defined or (UserInputs.nas_application_template.qos.policy.name != 'extreme' and UserInputs.nas_application_template.qos.policy.name != 'performance' and UserInputs.nas_application_template.qos.policy.name != 'value')) else UserInputs.nas_application_template.qos.policy.name }}"
export_policy: "{{ UserInputs.nas_application_template.export_policy.name | default(omit) }}"
nas_application_template:
use_nas_application: true
storage_service: "{{ UserInputs.nas_application_template.storage_service.name | default(omit) }}"
tiering:
control: "{{ UserInputs.nas_application_template.tiering.control | default(omit) }}"
policy: "{{ UserInputs.nas_application_template.tiering.policy | default(omit) }}"
object_stores: "{{ UserInputs.nas_application_template.tiering.object_stores[0] | default(omit) }}"
nfs_access: "{{ UserInputs.nas_application_template.nfs_access | default(omit) }}"
cifs_access: "{{ UserInputs.nas_application_template.cifs_access | default(omit) }}"
flexcache:
dr_cache: "{{ UserInputs.nas_application_template.flexcache.dr_cache | default(omit) }}"
origin_svm_name: "{{ UserInputs.nas_application_template.flexcache.origin_svm_name | default(omit) }}"
origin_component_name: "{{ UserInputs.nas_application_template.flexcache.origin_component_name | default(omit) }}"
<<: *login
- name: modify FlexCache prepopulate folders
when: UserInputs.nas_application_template.flexcache is defined and UserInputs.nas_application_template.flexcache.prepopulate.dir_paths is defined
na_ontap_flexcache:
state: present
name: "{{ UserInputs.name }}"
vserver: "{{ UserInputs.svm.name }}"
prepopulate:
dir_paths: "{{ UserInputs.nas_application_template.flexcache.prepopulate.dir_paths | default(omit) }}"
exclude_dir_paths: "{{ UserInputs.nas_application_template.flexcache.prepopulate.exclude_dir_paths | default(omit) }}"
recurse: "{{ UserInputs.nas_application_template.flexcache.prepopulate.recurse | default(omit) }}"
<<: *login
- name: Create SnapMirror relationship
when: UserInputs.snapmirror is defined
na_ontap_snapmirror:
state: present
policy: "{{ UserInputs.snapmirror.policy.name | default(omit) }}"
initialize: "{{ UserInputs.snapmirror.initialized }}"
source_endpoint:
cluster: "{{ UserInputs.snapmirror.source.cluster }}"
consistency_goup_volumes: "{{ UserInputs.snapmirror.source.consistency_group_volumes | default(omit)}}"
ipspace: "{{ UserInputs.snapmirror.source.ipspace | default(omit)}}"
path: "{{ UserInputs.snapmirror.source.path }}"
destination_endpoint:
cluster: "{{ UserInputs.snapmirror.destination.cluster.name }}"
consistency_goup_volumes: "{{ UserInputs.snapmirror.destination.consistency_group_volumes | default(omit) }}"
path: "{{ UserInputs.snapmirror.destination.path }}"
create_destination:
enabled: "{{ UserInputs.snapmirror.create_destination.enabled }}"
storage_service:
enabled: "{{ UserInputs.snapmirror.create_destination.storage_service.enabled }}"
enforce_performance: "{{ UserInputs.snapmirror.create_destination.storage_service.enforce_performance }}"
name: "{{ UserInputs.snapmirror.create_destination.storage_service.name | default(omit)}}"
tiering:
supported: "{{ UserInputs.snapmirror.create_destination.tiering.supported | default(omit) }}"
policy: "{{ UserInputs.snapmirror.create_destination.tiering.policy | default(omit) }}"
source_hostname: "{{ host.ip }}"
hostname: "{{ remoteClusterIP }}"
username: "{{ username }}"
password: "{{ password }}"
https: true
validate_certs: false
volumeAdd_variable.yaml
# 変数ファイルのキーの間隔をフォーマットしたり変更したりしないでください。 文字列値を二重引用符( "string" )で囲むことを推奨します。
host:
name: "localhost"
ip: 192.168.123.45
# 選択したオブジェクトに適用するプロパティがコメントに含まれていないことを確認します。
UserInputs:
name: "vol100"
# 整数値のみを指定してください。
size: 5368709120
# 「size unit」の値には、「bytes」、「b」、「kb」、「MB」、「GB」、「TB」、「PB」、「eb」、「ZB」、または「YB」を使用できます。デフォルト値は「GB」です。
size_unit: bytes
#auto_provision_as: "-"
svm:
name: "cifs100"
nas_application_template:
# FlexCache ボリュームに Snapshot ポリシーを設定できません。
snapshot_policy: "none"
export_policy:
name: "default"
tiering:
control: "disallowed"
#policy: ""
#object_stores:
#- ""
#storage_service:
#name: ""
qos:
policy:
name: "value"
# 固定 QoS の詳細を指定します。 新しいポリシーを作成するときは、固定 QoS の詳細を指定し、名前を追加します。
#fixed:
# QoS 制限の「最小 / 最大スループット」の値を MB/ 秒で指定します。
#capacity_shared:
#min_throughput_mbps: ""
#max_throughput_mbps: ""
#min_throughput_iops: ""
#max_throughput_iops: ""
#nfs_access:
#- access: "rw"
#host: "0.0.0.0/0"
#cifs_access:
#- access: "full_control"
#user_or_group: "Everyone"
#flexcache:
#dr_cache: true
# リモートボリュームの名前を指定します
#origin_component_name: ""
# リモートクラスタの選択した Storage VM にピア関係が設定されている必要があります。名前の競合が発生しないように、ピア Storage VM のローカル名を指定します。
#origin_svm_name: ""
#prepopulate:
#dir_paths:
#exclude_dir_paths:
#recurse: false
# SnapMirror のビジネス継続性機能は、このリリースではサポートされていません。
# Snapmirror inputs
# SnapMirror 関係を作成するには、リモートクラスタの IP を入力してください。
#remoteClusterIP:
#snapmirror:
#create_destination:
#enabled: true
#storage_service:
#enabled: true
#enforce_performance: true
#name:
#tiering:
#supported:
#policy:
#policy:
#name: ""
#initialized: "true"
#destination:
#cluster:
#name: ""
#path: ""
#source:
#cluster: ""
#path: ""
#ipspace: ""
4. Playbookの修正
実行環境で動作するように、以下の項目について修正を行います。
- volumeAdd.yamlのTaskのcreate FlexVolume or FlexGroupでnas_application_templateのtieringに関する内容を削除
- volumeAdd.yamlのvars_filesに記載した別ファイル名の変更(ファイル名を変えている場合)
- volumeAdd_variable.yamlのhostのipを操作対象のSVM管理IPに変更
Tieringが設定されてない環境でnas_application_templateのtieringを削除せずにPlaybookを実行すると以下のようなエラーとなります。
TASK [create FlexVolume or FlexGroup] ***********************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Error getting object store for aggregate: aggr1_node2: calling: storage/aggregates/XXXXX/cloud-stores: got {'message': 'not authorized for that command', 'code': '6'}."}
ファイル修正後のdiffコマンドによる比較は以下の通りです。
> diff volumeAdd_before.yaml volumeAdd_after.yaml
19c19
< - volumeAdd_variable.yaml
---
> - volumeAdd_variable_after.yaml
63,66d62
< tiering:
< control: "{{ UserInputs.nas_application_template.tiering.control | default(omit) }}"
< policy: "{{ UserInputs.nas_application_template.tiering.policy | default(omit) }}"
< object_stores: "{{ UserInputs.nas_application_template.tiering.object_stores[0] | default(omit) }}"
> diff volumeAdd_variable_before.yaml volumeAdd_variable_after.yaml
4c4
< ip: 192.168.123.45
---
> ip: 172.16.10.147
5. Playbookの実行
Playbook実行の際に、SVM上のアカウント入力が必要になりますので、入力を実施します。
> ansible-playbook -i inventory.yml -u user01 volumeAdd_after.yaml --ask-pass
SSH password:
Enter UserName: testuser
Enter Password:
PLAY [localhost] ********************************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************************
ok: [localhost]
TASK [Prompt remote cluster IP] *****************************************************************************************************
skipping: [localhost]
TASK [set_fact] *********************************************************************************************************************
skipping: [localhost]
TASK [set_fact] *********************************************************************************************************************
ok: [localhost]
TASK [Create or modify qos policy] **************************************************************************************************
skipping: [localhost]
TASK [create FlexVolume or FlexGroup] ***********************************************************************************************
changed: [localhost]
TASK [create FlexCache] *************************************************************************************************************
skipping: [localhost]
TASK [modify FlexCache prepopulate folders] *****************************************************************************************
skipping: [localhost]
TASK [Create SnapMirror relationship] ***********************************************************************************************
skipping: [localhost]
PLAY RECAP **************************************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=6 rescued=0 ignored=0
Storage側でVolumeが作成されている事を確認します。
> volume show -vserver cifs100 -volume vol100 -fields volume,size,qos-adaptive-policy-group
vserver volume size qos-adaptive-policy-group
------- ------ ---- -------------------------
cifs100 vol100 5GB value
参考及びリンク
Ansibleを使ってStorage(ONTAP)を管理する1 【Rocky LinuxへAnsible Install編】
Ansibleを使ってStorage(ONTAP)を管理する2【ChatGPTでVolume作成用Playbookを作成】