SoftLayerのネットワークストレージをAPIで見てみる


SoftLayerでは実に多様な種類のネットワークストレージを提供していて、Portalから簡単にオーダーして利用できます。

これらのネットワークストレージの情報はPortalから参照することもできますが、詳細を一覧で表示するためにスクリプトを作ってみました。実は、SoftLayer CLIの `sl iscsi list`コマンド や `sl iscsi detail <id>`コマンドでもほぼ同じ情報が取得できますが、

- 詳細まで一覧で見られるほうが便利
- CPSのボリュームは`sl iscsi list`コマンドでは表示されない

という理由から、その対応も含めて作成してみました。

## 仕組み

`Account.getNetworkStorage()`メソッドを呼び出して、ネットワークストレージの情報を取得して、必要なプロパティを取得しているだけの単純な仕組みです。このメソッドは、SoftLayerが提供する以下のネットワークストレージサービスの情報を返します。

- NAS
- Legacy iSCSI Storage
- Consistent Performance Storage
- Object Storage
- EVault Backup

QuantaStorやローカルストレージなどの情報は、このメソッドでは取得できません。


## スクリプト

```py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from prettytable import PrettyTable
import SoftLayer
import sluser

SL_USERNAME = sluser.SL_USERNAME
SL_API_KEY = sluser.SL_API_KEY

_maskNetworkStorage = '''
    hardware,
    iops,
    osTypeId,
    serviceResource,
    serviceResource.datacenter,
    serviceResourceName,
    serviceResourceBackendIpAddress,
    storageGroups,
    vendorName
    '''

_tableHeader = [
    'id',
    'nasType',
    'datacenter',
    'username',
    'gb',
    'iops',
    'serviceResourceName',
    'serviceResourceBackendIpAddress',
    'serviceResourceType',
    'hardware',
    'vendorName',
    'osTypeId'
    ]

def lookup(dic, key, *keys):
    if keys:
        return lookup(dic.get(key, {}), *keys)
    return dic.get(key)

client = SoftLayer.Client(username=SL_USERNAME, api_key=SL_API_KEY)
networkStorage = client['Account'].getNetworkStorage(mask=_maskNetworkStorage)

# Table definition
table = PrettyTable(_tableHeader)
table.padding_width = 1

for ns in networkStorage:
    table.add_row(
        [
            ns['id'],
            ns['nasType'],
            ns['serviceResource']['datacenter'].get('name'),
            ns['username'],
            ns['capacityGb'],
            lookup(ns, 'iops') or '-' ,
            ns['serviceResourceName'],
            ns['serviceResourceBackendIpAddress'],
            lookup(ns, 'serviceResource', 'type', 'type') or '-',
            lookup(ns, 'hardware') or '-',
            lookup(ns, 'vendorName') or '-',
            lookup(ns, 'osTypeId') or '-',
        ]
    )
print(table)
```

## 実行方法

```
python sl_getNetworkStorage.py
```

## 実行結果

![screenshot-sl-iscsi2.png](https://qiita-image-store.s3.amazonaws.com/0/65167/eef4cd71-6df9-794d-4667-d791cf8e07c2.png)


## ちなみに

ちなみに、`sl iscsi list`では、Legacy iSCSIのボリュームのみ表示されてCPSのボリュームは表示されませんが、`sl iscsi detail <id>`にCPSボリュームのIDを指定すると、CPSボリュームの詳細情報を取得できます(GitHubのsoftlayer-python v3.3.0)。ちょっと不思議な動作ですね。

これは`sl iscsi list`と`sl iscsi detail <id>`で、ボリュームの情報の取得に使っているAPIが異なるためです。

CLIの処理は直接APIを呼び出すのではなくManagerクラスを通して行われますが、iSCSIの場合は`ISCSIManager`というクラスを使用しています。

`sl iscsi list` で使われている`ISCSIManager.list_iscsi()`は、(上記のスクリプトで使っている`Account.getNetworkStorage()`ではなく) 内部で`Account.getIscsiNetworkStorage()`を呼び出していますが、このメソッドはLegacy iSCSIのボリュームのリストを返しますが、CPSのiSCSIボリュームは返しません。

```py:CLI/iscsi/list.py
    iscsi_mgr = SoftLayer.ISCSIManager(env.client)
    iscsi_list = iscsi_mgr.list_iscsi()
```

一方、`sl iscsi detail <id>`で使われている`ISCSIManager.get_iscsi()`は、内部で`Account.Network_Storage_Iscsi.getObject()`を呼び出しています。idを指定してこのメソッドを呼び出すと、Legacy iSCSIだけでなく、CPSボリュームの情報も返します。

```py:CLI/iscsi/detail.py
    iscsi_mgr = SoftLayer.ISCSIManager(env.client)

    iscsi_id = helpers.resolve_id(iscsi_mgr.resolve_ids, identifier, 'iSCSI')
    result = iscsi_mgr.get_iscsi(iscsi_id)
```


```py:managers/iscsi.py
class ISCSIManager(utils.IdentifierMixin, object):
    """Manages iSCSI storages."""

    def __init__(self, client):
        self.configuration = {}
        self.client = client
        self.iscsi_svc = self.client['Network_Storage_Iscsi']
        self.product_order = self.client['Product_Order']

    ...

    def list_iscsi(self):
        """List iSCSI volume."""
        account = self.client['Account']
        iscsi_list = account.getIscsiNetworkStorage(
            mask='eventCount,serviceResource[datacenter.name]')
        return iscsi_list

    def get_iscsi(self, volume_id, **kwargs):

        ...

        return self.iscsi_svc.getObject(id=volume_id, **kwargs)
```

この違いから上のような動作になっているわけです。
CPSボリュームも`sl iscsi list`で表示されるようにしてね、できたらストレージの種類もわかるといいな、ってissueをGitHubに上げておこうかと思います。


## 参考

- [NetworkSoftLayer_Network_Storage | SoftLayer Development Network](http://sldn.softlayer.com/reference/services/SoftLayer_Network_Storage)
- [NetworkSoftLayer_Network_Storage_Iscsi | SoftLayer Development Network](http://sldn.softlayer.com/reference/services/SoftLayer_Network_Storage_Iscsi)