QNAPのQTSは、いろんな設定をするのにqcli系のコマンドを使う。qcliを引数なしで実行すると、いろんなコマンドがあることを教えてくれる。
[~] # qcli
-v --version, display the version of QCLI and exit.
-h --help, print this help.
-l --login, login to check authentication.
qcli_admin, admin operations.
qcli_volume, volume operations.
qcli_pool, pool operations.
qcli_raid, RAID operations.
qcli_hdd, HDD operations.
qcli_cache, cache operations.
qcli_iscsi, iSCSI operations.
qcli_iscsiacl, iSCSI ACL operations.
qcli_iscsibackup, iSCSI backup operations.
qcli_virtualdisk, virtual disk operations.
qcli_power, power operations.
qcli_network, network operations.
qcli_log, log operations.
qcli_backuprestore, backup/restore operations.
qcli_firmwareupdate, firmware update operations.
qcli_sharedfolder, shared folder operations.
qcli_quota, quota operations.
qcli_networkservice, network service operations.
qcli_encrypt, encrypt operations.
qcli_rsyncserver, rsync server operations.
qcli_timemachine, time machine operations.
qcli_nastonas, nas to nas operations.
qcli_rsync, rsync operations.
qcli_networkrecyclebin, network recycle bin operations.
qcli_timezone, time zone operations.
qcli_domainsecurity, domain security operations.
qcli_users, users operations.
qcli_usergroups, usergroups operations.
qcli_ntp, NTP service operations.
qcli_hardware, hardware operations.
qcli_systemstatus, system status operations.
qcli_externaldevice, external device operations.
qcli_mysqlserver, mysqlserver operations.
qcli_volumesnapshot, volume snapshot operations.
qcli_iscsisnapshot, iSCSI snapshot operations.
qcli_domaincontroller, domain controller operations.
qcli_snapreplica, SnapReplica operations.
qcli_snapshotvault, Snapshot Vault operations.
QCLI 4.4.2 20200413, QNAP Systems, Inc.
これをAnsibleからcommandで叩けば……と思ったけれど、事はそれほど単純ではなかった。
qcli の実行手順
qcli系のコマンドを使うには、いったんログインしなくてはならず、そこで返ってくるSIDを各コマンドで入力しなくてはならない。
[~] # qcli -l user=admin pw=PASSWORD
Authentication success!
sid is 12345678
[~] # qcli_hardware -B sid=12345678
system_operation Disabled
system_events Disabled
Ansibleのcommandをこの順番で実行して、結果を保存して……とやるよりは、モジュールを作る方が早い(本当にAnsibleのモジュールは簡単なので、whenとかregisterとかいろいろ考えるよりはモジュールの方がいいと思う)。
モジュールを作る
qcli_login でログインして、 qcli でコマンドを実行するようにする。例によって未テスト・ドキュメントなし・詰めは甘い。
#!/usr/bin/python
import re
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'iwatam'
}
DOCUMENTATION = '''
'''
EXAMPLES = '''
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
user=dict(type='str',required=False,default='admin'),
password=dict(type='str',required=False,no_log=True)
),
supports_check_mode=False
)
user=module.params['user']
password=module.params['password']
rc,out,err=module.run_command(['qcli','-l','user='+user,'pw='+password])
m=re.search('sid is (\\w+)',out)
if m is None:
module.fail_json(msg='Commmand parse error or login failed.',
out=out)
sid=m.groups()[0]
module.exit_json(changed=False,
sid=sid,
output=out,
ansible_facts=dict(ansible_qcli_sid=sid))
if __name__ == '__main__':
main()
user と password を渡して実行すると、ログインしてansible_qcli_sidという名前でfactとしてsidを格納する。必ずChanged=Falseで返る。
#!/usr/bin/python
import re
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'iwatam'
}
DOCUMENTATION = '''
'''
EXAMPLES = '''
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
query_cmd=dict(type='str',required=True),
expects=dict(type='str',required=True),
modify_cmd=dict(type='str',required=True),
sid=dict(type='str',required=False)
),
supports_check_mode=False
)
query_cmd=module.params['query_cmd']
expects=module.params['expects']
modify_cmd=module.params['modify_cmd']
sid=module.params['sid']
sidopt=" sid="+sid
rc, out, err=module.run_command(query_cmd+sidopt,check_rc=True)
changed=False
if not re.fullmatch(expects,out):
rc, out2, err=module.run_command(modify_cmd+sidopt,check_rc=True)
changed=True
module.exit_json(changed=changed,
sid=sid,
original_output=out,
ansible_facts=dict(ansible_qcli_sid=sid),
output='')
if __name__ == '__main__':
main()
query_cmd, expects, modify_cmd, sid の4つを指定する。最初にquery_cmdを(後ろにsid=XXXXを付けて)実行して、その結果がexpectsで指定した正規表現と完全にマッチするならOK、しないならmodify_cmdを実行する。
Ansible のplaybookは以下のようになる。
- name: login to qcli
qcli_login:
password: "{{ password }}"
- name: add user
qcli:
query_cmd: "qcli_users -c username=iwatam"
expects: 'User\s+name\s+exist!\s*'
modify_cmd: "qcli_users -a username=iwatam password=PASSWORD passwordVerify=PASSWORD"
sid: "{{ ansible_qcli_sid }}"
個人的なヤツなので、passwordが平文で書かれるのがどうとか面倒くさいことは言わない。
イケてないところ
- いちいちsidを渡さないといけない。(モジュール呼び出しの間で値を共有する機構を発見できなかった)
- passwordが平文で書かれるのはどうなのか
- コマンドごとにモジュールを作るのが一番カッコイイんだろうけど、個人でやるには面倒すぎる。
- というか、そういうモジュールは既にあるんじゃないかと思うんだけど。