NutanixをREST APIで操作する その2

# NutanixをREST APIで操作する その2

* 大規模環境となると少なくとも自動化・スクリプト化の要件があり、今までperlやPowerShellなどのスクリプト、SNMPなどで行ってきましたが、最近REST APIの需要も増えてきているかと思います。
* REST APIってよくわからない状態から確認していきます。
* 前回はREST APIでどのようなことができるかPrismの画面から見てみました。
* 今回はPythonで操作してみます。



## 目次

* 下記の流れでまとめてます
  * [PrismでREST APIを見てみる（前回）](https://qiita.com/matsumo2019/items/dc467f8f7b7bc89dde49)
  * PythonでREST APIを操作してみる（今回)



## スクリプト概要

### 目的

* 特定のクラスタへアクセスして指定したREST APIの結果を表示する
* クラスタ情報は変更することが多いため設定ファイルファイルとして別ファイルで指定する

### 

### プログラムの流れ

* 設定ファイルを読み込む
  * conf.json
  * rest-api_list.txt
* Clusterに対してREST APIを発行する
* 特定のキーのみカンマ区切りで出力
* 要素がなくなるまで出力する



## 少しずつ作っていく

### メイン処理の確認用スクリプト

* 拾ってきたものをコピペ

```python
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
import json

IP = '10.42.8.37'
USER = 'admin'
PASSWORD = 'hogehoge'

# (1) Make Session
session = requests.Session()
session.auth = (USER, PASSWORD)
session.verify = False                              
session.headers.update({'Content-Type': 'application/json; charset=utf-8'})

# (2) Make URL
url = 'https://{}:9440/PrismGateway/services/rest/v1/cluster'.format(IP)

# (3) Send request and get Response
response = session.get(url)

# (4) Check response code
print('Response Code: {}'.format(response.status_code))
print('Response OK?: {}'.format(response.ok))

# (5) Check response body
print('Response Body:')
print(response.text)


```



## スクリプト説明

### import処理。http通信を利用して何かするときのセット

* HTTP 通信ライブラリの**Requests**。 
* URLを扱うモジュールを集めたurllib3
* オレオレ証明書を無視するおまじない
* まだ使わないがどうせ使うjsonモジュール

```python
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
import json
```



###  セッションオブジェクトの作成、初期化

* IP/ユーザ名/パスワードは最終的に別ファイル、もしくはユーザから入力させる。ひとまず定数として定義
* requestsモジュールからセッションオブジェクトを作成し、そこに情報（ユーザIDパスワードなど）をつけていく
* URLはのちに変数や別ファイルにするとして、ひとまず処理中に記載
* ここではまだ、送信されていない

```python
IP = '10.42.8.37'
USER = 'admin'
PASSWORD = 'hogehoge'

# (1) Make Session
session = requests.Session()
session.auth = (USER, PASSWORD)
session.verify = False                              
session.headers.update({'Content-Type': 'application/json; charset=utf-8'})

# (2) Make URL
url = 'https://{}:9440/PrismGateway/services/rest/v1/cluster'.format(IP)
```



### セッションの実行

* session.get(url)で実際に通信を行う。
* デバッグ用・条件分岐用にサーバからのレスポンスコードを取得する。
* print(response.text)取得した結果を確認する。

```python
# (3) Send request and get Response
response = session.get(url)

# (4) Check response code
print('Response Code: {}'.format(response.status_code))
print('Response OK?: {}'.format(response.ok))

# (5) Check response body
print('Response Body:')
print(response.text)
```

## 



## 出来上がったスクリプト

* get_vms.py
* 上記のスクリプトから少し変わっていますが、最終的に出力させただけです。
* 標準出力ではきれいに出ませんが、CSVとして使用できるようにしました。
* 今後のデバッグのために一部コメントアウトを残しています

```python
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
import json
import base64

# ファイルの読み込み
fin = open('cvm_info.json', 'r')

# サーバ情報の読み込み
# IP = '10.42.8.37'
# USER = 'admin'
# PASSWORD = 'pass'

sv_info = json.load(fin)
IP = sv_info['cluster_address']
USER = sv_info['user_name']
PASSWORD = sv_info['cvm_password']
fin.close()

auth_str = USER + ':' + PASSWORD
auth_str = api_head = {
    'Authorization': 'Basic ' + base64.b64encode(auth_str.encode('utf-8')).decode(),
}

# (1) Make Session
session = requests.Session()
session.auth = (USER, PASSWORD)
session.verify = False                              
session.headers.update({'Content-Type': 'application/json; charset=utf-8'})

# (2) Make URL
url = 'https://{}:9440/PrismGateway/services/rest/v2.0/vms/'.format(IP)

# (3) Send request and get Response
response = session.get(url, headers=api_head)

# (4) Check response code
# print('Response Code: {}'.format(response.status_code))
# print('Response OK?: {}'.format(response.ok))

# (5) Check response body
# print('Response Body:')
# print(response.text)


# (6) text -> dict
dict_data = json.loads(response.text)

# (7) VM一覧を出力 CSV
i = 0
while i < dict_data['metadata']['total_entities']:
    vms= (
        dict_data['entities'][i]['name'], 
        dict_data['entities'][i]['description'], 
        dict_data['entities'][i]['num_cores_per_vcpu'],
        dict_data['entities'][i]['num_vcpus'],
        dict_data['entities'][i]['memory_mb'], 
        dict_data['entities'][i]['uuid']
        )
    print(*vms,sep=',')
    i += 1
```

## 

### 補足

* Basic認証する場合はutf-8でエンコードする必要がありました

```python
auth_str = USER + ':' + PASSWORD
auth_str = api_head = {
    'Authorization': 'Basic ' + base64.b64encode(auth_str.encode('utf-8')).decode(),
}
```



* 元々の情報量が多かったので、必要な情報VM Name、description、cpu数、メモリ数、uuidのみ表示させています。
* VM数はtotal_entitiesで取れたので、それをもとに回しています。
* 一度、タプル型（vms)に渡していますが、もっと奇麗に書けそう。
* REST APIの仕様が変わると、ここを直接修正しないといけないので、メンテナンス性に欠ける...

```python
# (6) text -> dict
dict_data = json.loads(response.text)

# (7) VM一覧を出力 CSV
i = 0
while i < dict_data['metadata']['total_entities']:
    vms= (
        dict_data['entities'][i]['name'], 
        dict_data['entities'][i]['description'], 
        dict_data['entities'][i]['num_cores_per_vcpu'],
        dict_data['entities'][i]['num_vcpus'],
        dict_data['entities'][i]['memory_mb'], 
        dict_data['entities'][i]['uuid']
        )
    print(*vms,sep=',')
    i += 1
```

## 

### 実行結果

* Basic認証する場合はutf-8でエンコードする必要がありました
* 元々の情報量が多かったので、必要な情報VM Name、description、cpu数、メモリ数、uuidのみ表示させています。
* 構築作業後に取得するのに使えそうですね

```python
PS C:\Users\oresama>  get_aos_rest.py
Move VM,RX-Autodeployed-VM,4,1,8192,ffac0fb7-6a0b-45b1-a3ea-7c8079af2364
RXAutomationPC,NutanixPrismCentral,1,4,16384,f6817073-c941-4504-8034-a1d776b03a4c
Windows 2012 VM,RX-Autodeployed-VM,4,1,8192,23647618-2d45-46ac-8909-ea1444be6d94
Windows 10 VM,RX-Autodeployed-VM,4,1,8192,59a768a1-556d-4c17-bd84-71875a666d99
AutoDC VM,RX-Autodeployed-VM,4,1,8192,68eef805-3671-4717-99d6-5a07723da365
CentOS VM,RX-Autodeployed-VM,4,1,8192,cd9d44b4-db7a-4582-91f0-44d5d27aff2b
```

## 

* ちな、元のデータ

```
{"metadata":{"grand_total_entities":6,"total_entities":6,"count":6,"start_index":0,"end_index":6},"entities":[{"allow_live_migrate":true,"gpus_assigned":false,"boot":{"disk_address":{"device_bus":"scsi","device_index":0},"boot_device_type":"disk"},"description":"RX-Autodeployed-VM","ha_priority":0,"memory_mb":8192,"name":"Move VM","num_cores_per_vcpu":4,"num_vcpus":1,"power_state":"off","timezone":"UTC","uuid":"ffac0fb7-6a0b-45b1-a3ea-7c8079af2364","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":1,"machine_type":"pc"},{"allow_live_migrate":true,"gpus_assigned":false,"description":"NutanixPrismCentral","ha_priority":0,"host_uuid":"fab5dfe4-f92d-4ce8-b296-4790dd19f4c8","memory_mb":16384,"name":"RXAutomationPC","num_cores_per_vcpu":1,"num_vcpus":4,"power_state":"on","timezone":"UTC","uuid":"f6817073-c941-4504-8034-a1d776b03a4c","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":2,"machine_type":"pc"},{"allow_live_migrate":true,"gpus_assigned":false,"boot":{"disk_address":{"device_bus":"scsi","device_index":0},"boot_device_type":"disk"},"description":"RX-Autodeployed-VM","ha_priority":0,"memory_mb":8192,"name":"Windows 2012 VM","num_cores_per_vcpu":4,"num_vcpus":1,"power_state":"off","timezone":"UTC","uuid":"23647618-2d45-46ac-8909-ea1444be6d94","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":1,"machine_type":"pc"},{"allow_live_migrate":true,"gpus_assigned":false,"boot":{"disk_address":{"device_bus":"scsi","device_index":0},"boot_device_type":"disk"},"description":"RX-Autodeployed-VM","ha_priority":0,"host_uuid":"fee3e23a-2c0f-4a51-963e-289601935fd3","memory_mb":8192,"name":"Windows 10 VM","num_cores_per_vcpu":4,"num_vcpus":1,"power_state":"on","timezone":"UTC","uuid":"59a768a1-556d-4c17-bd84-71875a666d99","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":2,"machine_type":"pc"},{"allow_live_migrate":true,"gpus_assigned":false,"boot":{"disk_address":{"device_bus":"scsi","device_index":0},"boot_device_type":"disk"},"description":"RX-Autodeployed-VM","ha_priority":0,"host_uuid":"fee3e23a-2c0f-4a51-963e-289601935fd3","memory_mb":8192,"name":"AutoDC VM","num_cores_per_vcpu":4,"num_vcpus":1,"power_state":"on","timezone":"UTC","uuid":"68eef805-3671-4717-99d6-5a07723da365","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":2,"machine_type":"pc"},{"allow_live_migrate":true,"gpus_assigned":false,"boot":{"disk_address":{"device_bus":"scsi","device_index":0},"boot_device_type":"disk"},"description":"RX-Autodeployed-VM","ha_priority":0,"memory_mb":8192,"name":"CentOS VM","num_cores_per_vcpu":4,"num_vcpus":1,"power_state":"off","timezone":"UTC","uuid":"cd9d44b4-db7a-4582-91f0-44d5d27aff2b","vm_features":{"AGENT_VM":false,"VGA_CONSOLE":true},"vm_logical_timestamp":1,"machine_type":"pc"}]}
```



## まとめ

* REST APIを使用してNutanixのClusterの情報を取得することができました。
* 今回は下記を学びました。今回はデータの参照でしたが同じようにスナップショットの取得もできそうです。
  * REST APIの実行（URLの操作）
  * JSONデータの整形
* Nutanixは各VMのuuidを元にデータ管理しているので、ここでの方法でVM名とuuidが取得できるので再利用できるかと思います