IBM CloudのSoftLayer APIを使って、ハンズオン環境の構築を自動化してみた

#SoftLayer API

IBMのクラウドソリューションであるBluemix。2016年10月にはIaaSのレイヤーとPaaSのレイヤーが統合され、Bluemixという一つのソリューションであらゆるサービスレンジに対応できるようになりました。

そのインフラ部分、つまりサーバーやネットワーク、ストレージの部分を提供しているのがBluemix Infrastructureです。その特徴の一つとして、API(SoftLayer API)による管理の自動化が挙げられます。

Bluemix Infrastructureでは、物理サーバーのデプロイやユーザーの作成はもちろん、果てはDNSへの登録、各データセンターの位置情報取得やサポートチケットの発行に至るまで、あらゆる操作をAPIを通して行うことが可能になっています。

それもそのはずで、実はBluemix Infrastructureの利用者向けに提供されるカスタマーポータルもAPIを利用して作成されているため、ポータルで行うことのできる動作は全てAPIが提供されていることになります。

Web上にもSoftLayer APIに関する記事はいくつか上がっておりますので、そちらもご参照ください。
こちらにまとまっています。
[SoftLayer APIプログラミングの参考サイト](http://qiita.com/takech9203/items/4ae4ea905a6bb43e09a3)


#前置き

2016年2月、IBMはVMware社と協業し、Bluemix上でVMwareのコンポーネントがフルスタックで利用できるようになりました。
その関係で、IBM単体で、もしくはソフトバンク コマース＆サービス様をはじめとしたBluemix Infrastructureのパートナー様とVMware on IBM Cloudのハンズオンを開催する機会が多々あります。

[vSphere "6.5" on IBM Bluemix Infrastructure(SoftLayer) ハンズオン](https://www.ibm.com/cloud-computing/jp/ja/community/cloud-service.html)
例えば上記のようなハンズオンを定期的に行なっておりますので、是非お越しください。

しかし、ここで問題となるのがハンズオン環境の構築です。ハンズオンでは、参加者一人に対して、ベアメタル・サーバーと作業用の仮想サーバーをそれぞれ1台ずつ用意します。まだ参加者の人数が少なければ良いのですが、参加者の人数が多くなった時、手作業では大変です。

それ以外にも、VLANの設定やチケットの発行など細かい作業が多く、手作業だとミスが起こります。(ごめんなさい・・・)
そこで今回はハンズオン環境の構築を自動化し、少しでもミスを少なくし、私の負担を減らしてみようと思います。


#ハンズオン環境を構築するために必要な作業

ハンズオン環境は簡単に以下のような構成になっています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103277/ee2c4ef3-a919-5a1d-1151-ea7b55f73188.png)

参加者は自分に割り当てられた作業用の仮想サーバーにSSL VPNを経由してリモートログインし、その仮想サーバー上からベアメタル・サーバーにESXiやvCenterをインストールします。
そのため、ベアメタル・サーバーと仮想サーバーは同じVLAN上にある必要があります。
また、インターネットは利用しないため、Public VLANには接続する必要はないので、Private Onlyでオーダーします。


環境構築のための作業を下記に列挙します。


1. 人数分のIBMid用メールアドレスを用意
2. ハンズオン用アカウントの子ユーザーとして参加者用のユーザーを作成
    * ユーザーの権限を設定
3. 人数分の時間課金BMS(ベアメタル・サーバー)を注文
    * 他の環境とVLANを分ける必要があるため、海外DCを選択
        * **時間課金のBMSはVLANの指定ができない**ため、利用していない海外のDCを選択してVLANを分割
    * 構成は下記のものとする
        * No Operating System(OSなし→参加者にハンズオンとしてESXiを導入してもらうため)
        * Private Only(インターネットへアクセスしない)
        * NICのポート速度は1Gbps
        * ホスト名: handson.bms1.softlayer.com, handson.bms2.softlayer.com・・・
4. BMSの設定変更のためにチケットを発行する
    * IPMIの権限設定をOperatorからAdministratorに変更
    * ISOブートの優先度をあげる
    * UEFIの解像度をLegacy only modeへ変更
5. BMSと同じVLAN上にイメージテンプレートから時間課金のVSI(仮想サーバー)を人数分立てる
    * OSには既存のイメージを指定
    * Private Only(こちらもインターネットへのアクセス不要)
    * NIC速度は1Gbps
    * ホスト名: handson.vsi1.softlayer.com, handson.vsi2.softlayer.com・・・
6. 参加者用のユーザーにオーダーしたBMSとVSIをそれぞれ割り当てる
7. BMS, VSIと同じVLAN上にVM用のPrivate Portable IPを用意



かなり多くの作業が必要です。時間がかかり、入力間違いや選択ミスなどで前日は夜も眠れません。そこでこれらの作業を全て自動化しようと思います。

今回はPythonを利用してスクリプトを書いていきます。
ちなみに私はPythonに関して詳しくないため、こっちの書き方の方がいいよとかあったらこっそり教えてください。

*また、APIは仕様が変更になる可能性があります。そのため、同じスクリプトでも同じ結果が得られないこともあり、予期せぬ動作をする場合もありますので、ご留意ください。*

#スクリプトの作成

ひとまず完成したスクリプトが下記になります。

```py:handson.py
# coding:utf-8
#Prepare for hands-on environment

import SoftLayer 
import json
import SoftLayer.API
import sys
import csv
from time import sleep

#API key of master account
username = "username"
apiKey = "apikey"

client = SoftLayer.Client(username= username, api_key = apiKey)
bms_allhosts = ''
counter  = 0

#Import csv file
csvfile = "list.csv"
f = open(csvfile, "rU")
reader = csv.reader(f)
header = next(reader)
 
for (num,row) in enumerate(reader):
    
    #Count numbers
    counter += 1
    
    
    ##############################################################################################
    #Create new user
    ##############################################################################################
    
    
    #Input user information
    user_template = {
        'username': row[0],
        'firstName': row[1],
        'lastName': row[2],
        'email': row[3],
        'companyName': 'company name',
        'address1': 'address',
        'city': 'city',
        'country': 'JP',
        'state': 'OT',
        'postalCode': 'xxx-xxxx',
        'officePhone': row[5],
        'userStatusId': 1001,
        'timezoneId': 158,
        'localeId': 22,
        'sslVpnAllowedFlag': True
    }
    
    
    #Create new user
    try:
        new_user = client['User_Customer'].createObject(user_template, row[4], row[4])
        print("complete user creation!")
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()

        
        
        
        
    ##############################################################################################
    #Set permissions
    ##############################################################################################

    
    #Define permission template    
    permission_template = [
        {'keyName':'ACCOUNT_SUMMARY_VIEW'},
        {'keyName':'ANTI_MALWARE_MANAGE'},
        {'keyName':'BANDWIDTH_MANAGE'},
        {'keyName':'CDN_BANDWIDTH_VIEW'},
        {'keyName':'DEDICATED_HOST_VIEW'},
        {'keyName':'DNS_MANAGE'},
        {'keyName':'FIREWALL_MANAGE'},
        {'keyName':'FIREWALL_RULE_MANAGE'},
        {'keyName':'HARDWARE_VIEW'},
        {'keyName':'HOST_ID_MANAGE'},
        {'keyName':'IP_ADD'},
        {'keyName':'LICENSE_VIEW'},
        {'keyName':'LOADBALANCER_MANAGE'},
        {'keyName':'MONITORING_MANAGE'},
        {'keyName':'NETWORK_MESSAGE_DELIVERY_MANAGE'},
        {'keyName':'NTF_SUBSCRIBER_MANAGE'},
        {'keyName':'PORT_CONTROL'},
        {'keyName':'REMOTE_MANAGEMENT'},
        {'keyName':'SERVER_ADD'},
        {'keyName':'SOFTWARE_FIREWALL_MANAGE'},
        {'keyName':'TICKET_EDIT'},
        {'keyName':'TICKET_SEARCH'},
        {'keyName':'TICKET_VIEW'},
        {'keyName':'TICKET_VIEW_BY_HARDWARE'},
        {'keyName':'TICKET_VIEW_BY_VIRTUAL_GUEST'},
        {'keyName':'VIRTUAL_GUEST_VIEW'},
        {'keyName':'VPN_MANAGE'},
        {'keyName':'VULN_SCAN_MANAGE'}
    ]
    
    
    #Set permissions
    try:
        client['User_Customer'].addBulkPortalPermission(permission_template, id=new_user['id'])
        print("complete setting user permission!") 
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()
    
    
    
    
    
    ##############################################################################################
    #Order hourly BMS
    ##############################################################################################
    
    
    #Set BMS order template
    bare_metal_server = {
        'datacenter': {'name': 'hkg02'},
        'hostname': 'handson.bms' + str(num+1),
        'domain': 'softlayer.com',
        'hourlyBillingFlag': True,
        'fixedConfigurationPreset': {'keyName': 'S1270_32GB_1X1TBSATA_NORAID'},
        'networkComponents': [{
            'maxSpeed': 1000,
        }],
        'privateNetworkOnlyFlag': True,
        'operatingSystemReferenceCode': 'CUSTOS_1_64'
    }
    
    #Order BMS
    try:
        order_bms = client['Hardware'].createObject(bare_metal_server)
        print("complete order BMS!")
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()
        
    #Add FQDN for ticket contents
    bms_allhosts = bms_allhosts + (order_bms['hostname'] + order_bms['domain'] + '\n')
        
        
        
        
    ##############################################################################################
    #Retrieve BMS information
    ##############################################################################################
    
    
    #Get BMS id from Global ID
    mask_1 = "mask[id;globalIdentifier]"
    new_bms_id = 0
    
    while True:
        bms_infor = client['Account'].getHardware(mask=mask_1)
        for bms_id in bms_infor:
            try:
                if bms_id['globalIdentifier'] == order_bms['globalIdentifier']:
                    new_bms_id = bms_id['id']
                    break
            except KeyError:
                print('please wait for a minutes... now provisioning BMS...')
                sleep(120)
        if new_bms_id > 0:
            break
    
    print(new_bms_id)
    
    #Get BMS private VLAN ID
    mask_2 = "mask[id]"
    bms_vlan_id = 0
    
    while True:
        bms_vlan = client['Hardware'].getNetworkVlans(id=new_bms_id,mask=mask_2)
        bms_vlan_id = bms_vlan[0]['id']
        if bms_vlan_id > 0:
            break
    

    
    ##############################################################################################
    #Order hourly VSI
    ##############################################################################################

    
    #Set VSI order template
    virtual_server = {
        'datacenter': {'name': 'hkg02'},
        'hostname': 'handson.vsi' + str(num+1),
        'domain': 'softlayer.com',
        'hourlyBillingFlag': True,
        'startCpus': 2,
        'maxMemory': 4096,
        'blockDeviceTemplateGroup':{
            'globalIdentifier': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
        },
        'localDiskFlag': False,
        'primaryBackendNetworkComponent':{
            'networkVlan':{
                'id': bms_vlan_id
            }
        },
        'networkComponents': [{
            'maxSpeed': 1000,
        }],
        'privateNetworkOnlyFlag': True,
    }
    
    #Order VSI
    try:
        order_vsi = client['Virtual_Guest'].createObject(virtual_server)
        print("complete order VSI!") 
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()
        
        
        
        
        
    ##############################################################################################
    #Retrieve VSI information
    ##############################################################################################
    
    
    #Get VSI id from Global ID
    mask_3 = "mask[id;globalIdentifier]"
    new_vsi_id = 0

    while True:
        vsi_infor = client['Account'].getVirtualGuests(mask=mask_3)
        for vsi_id in vsi_infor:
            try:
                if vsi_id['globalIdentifier'] == order_vsi['globalIdentifier']:
                    new_vsi_id = vsi_id['id']
                    break
            except KeyError:
                print('please wait for a minutes... now provisioning VSI...')
                sleep(60)
        if new_vsi_id > 0:
            break
    
    
    
    
    ##############################################################################################
    #Server access control 
    ##############################################################################################    
    
    
    
    #Remove all access
    client['User_Customer'].removeAllHardwareAccessForThisUser(id=new_user['id'])
    client['User_Customer'].removeAllVirtualAccessForThisUser(id=new_user['id'])
    
    #Create lists of new server id
    new_bms_idset = [new_bms_id]
    new_vsi_idset = [new_vsi_id]
    
    #Add specific server access
    client['User_Customer'].addBulkHardwareAccess(new_bms_idset,id=new_user['id'])    
    client['User_Customer'].addBulkVirtualGuestAccess(new_vsi_idset,id=new_user['id'])
    
    
f.close()


##############################################################################################
#Issue ticket 
##############################################################################################

#Create ticket template
ticket_template = {
    'subjectId': 1021, # Hardware Issue
    'assignedUserId': userid,
    'title': 'IPMI privilege and UEFI mode'
}
ticket_contents = 'I have three requests. I would like to boot server from ISO and install vSphere(BYOL).\nI want to change the setting of following ' + counter + ' servers.\n\n' + bms_allhosts + '1. Could you change the privilege of IPMI control from Operator to Administrator?\n2. Please raise a start priority of ISO.\n3. Please change the IPMI\'s UEFI mode into legacy only mode.\n\nI agree to enforcement if you need to reboot the servers'

#Issue ticket
created_ticket = client['Ticket'].createStandardTicket(ticket_template, ticket_contents)


```

また、同じフォルダに参加者一覧の.csvファイルを置いておきます。


```csv:list.csv
Username,First Name,Last Name,IBMid,password,office phone
test1,Toro,Yamada,xxx-1@xxx.com,password1,xx-xxxx-xxxx
test2,Jiro,Takahashi,xxx-2@xxx.com,password1,xxx-xxxx-xxxx
・・・
```

簡単なスクリプトの流れとしては、csvファイルを1行読み込み、そこに記載されているユーザーを作成、権限を設定し、そのユーザーに割り当てるベアメタル・サーバーと仮想サーバーをオーダーします。
それを最終行まで繰り返すような形になっています。

ではここからは部分毎にAPIの使い方について解説していきます。また、SoftLayer APIの基本的な考え方と簡単な用語(サービス、メソッド、データタイプ、オブジェクトマスク等)は理解していることを前提とします。


##csvファイルの読み込み

```py:handson.py
#Import csv file
csvfile = "list.csv"
f = open(csvfile, "rU")
reader = csv.reader(f)
header = next(reader)
 
for (num,row) in enumerate(reader):
```

ここでは、参加者のリストファイルを読み込み、1行ずつ処理していきます。1行目はヘッダーとして、読み飛ばす処理を入れています。

##ユーザーの作成

```py:handson.py
    ##############################################################################################
    #Create new user
    ##############################################################################################
    
    
    #Input user information
    user_template = {
        'username': row[0],
        'firstName': row[1],
        'lastName': row[2],
        'email': row[3],
        'companyName': 'company name',
        'address1': 'address',
        'city': 'city',
        'country': 'JP',
        'state': 'OT',
        'postalCode': 'xxx-xxxx',
        'officePhone': row[5],
        'userStatusId': 1001,
        'timezoneId': 158,
        'localeId': 22,
        'sslVpnAllowedFlag': True
    }
    
    
    #Create new user
    try:
        new_user = client['User_Customer'].createObject(user_template, row[4], row[4])
        print("complete user creation!")
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()

```

ここではUser_CustomerサービスのcreateObjectメソッドを用いて作成します。このサービスのcreateObjectでは下記の3つのパラメータを入力することが可能です。

| パラメータ | 概要 |データ型|
|:-:|:-:|:-:|
|templateObject|ユーザー作成時のテンプレート。|[User_Customer](http://sldn.softlayer.com/reference/datatypes/SOftLayer_User_Customer)|
|password|アカウントのパスワード。|string|
|vpnPassword|SSL VPNを利用するときのパスワード。|string|

User_Customerデータタイプは、Pythonで言う所の辞書オブジェクトのような形になっており、使用できるキーと値は[こちら](http://sldn.softlayer.com/reference/datatypes/SOftLayer_User_Customer)に記載されています。

このユーザーテンプレートの作成時にcsvファイルから読み込んだデータを埋め込んでいます。

また、passwordパラメータは「VPNのみのユーザー」や「IBMidで作成されるユーザー」では無視されます。新規に作成されるユーザーは全てIBMidによる認証を利用することになっているため、ここは無視されます。
(ちなみにその場合は24時間以内にユーザーの方でパスワードの初期設定処理が必要になります。)


##ユーザー権限の設定

```py:handson.py
    ##############################################################################################
    #Set permissions
    ##############################################################################################

    
    #Define permission template    
    permission_template = [
        {'keyName':'ACCOUNT_SUMMARY_VIEW'},
        {'keyName':'ANTI_MALWARE_MANAGE'},
        {'keyName':'BANDWIDTH_MANAGE'},
        {'keyName':'CDN_BANDWIDTH_VIEW'},
        {'keyName':'DEDICATED_HOST_VIEW'},
        {'keyName':'DNS_MANAGE'},
        {'keyName':'FIREWALL_MANAGE'},
        {'keyName':'FIREWALL_RULE_MANAGE'},
        {'keyName':'HARDWARE_VIEW'},
        {'keyName':'HOST_ID_MANAGE'},
        {'keyName':'IP_ADD'},
        {'keyName':'LICENSE_VIEW'},
        {'keyName':'LOADBALANCER_MANAGE'},
        {'keyName':'MONITORING_MANAGE'},
        {'keyName':'NETWORK_MESSAGE_DELIVERY_MANAGE'},
        {'keyName':'NTF_SUBSCRIBER_MANAGE'},
        {'keyName':'PORT_CONTROL'},
        {'keyName':'REMOTE_MANAGEMENT'},
        {'keyName':'SERVER_ADD'},
        {'keyName':'SOFTWARE_FIREWALL_MANAGE'},
        {'keyName':'TICKET_EDIT'},
        {'keyName':'TICKET_SEARCH'},
        {'keyName':'TICKET_VIEW'},
        {'keyName':'TICKET_VIEW_BY_HARDWARE'},
        {'keyName':'TICKET_VIEW_BY_VIRTUAL_GUEST'},
        {'keyName':'VIRTUAL_GUEST_VIEW'},
        {'keyName':'VPN_MANAGE'},
        {'keyName':'VULN_SCAN_MANAGE'}
    ]
    
    
    #Set permissions
    try:
        client['User_Customer'].addBulkPortalPermission(permission_template, id=new_user['id'])
        print("complete setting user permission!") 
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()

```


次に、作成したユーザーにポータルの権限を設定します。権限の設定にはUser_CustomerサービスのaddBulkPortalPermissionメソッドを使います。

このメソッドには設定したい権限のリストをパラメーターとして代入します。
また、テンプレートの中のkeyNameの値として指定できる名前は、User_Customer_CustomerPermission_PermissionサービスのgetAllObjectsメソッドで[一覧表示可能](https://github.com/yukitada/softlayerapi/blob/master/permissions)です。

##時間課金ベアメタル・サーバーのオーダー

```py:handson.py
    ##############################################################################################
    #Order hourly BMS
    ##############################################################################################
    
    
    #Set BMS order template
    bare_metal_server = {
        'datacenter': {'name': 'hkg02'},
        'hostname': 'handson.bms' + str(num+1),
        'domain': 'softlayer.com',
        'hourlyBillingFlag': True,
        'fixedConfigurationPreset': {'keyName': 'S1270_32GB_1X1TBSATA_NORAID'},
        'networkComponents': [{
            'maxSpeed': 1000,
        }],
        'privateNetworkOnlyFlag': True,
        'operatingSystemReferenceCode': 'CUSTOS_1_64'
    }
    
    #Order BMS
    try:
        order_bms = client['Hardware'].createObject(bare_metal_server)
        print("complete order BMS!")
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()
        
    #Add FQDN for ticket contents
    bms_allhosts = bms_allhosts + (order_bms['hostname'] + order_bms['domain'] + '\n')
        
        
```

時間課金のベアメタル・サーバーはあらかじめプリセットされており、いくつかのテンプレートがあらかじめ構成されています。

ここではHardwareサービスのcreateObjectメソッドを使用しています。ちなみに月額課金のよりカスタマイズ性の高いベアメタル・サーバーをオーダーする場合は[Container_Product_Order](http://sldn.softlayer.com/reference/datatypes/SoftLayer_Container_Product_Order)データタイプを利用し、より多くのオプションを設定する必要があります。

createObjectメソッドにはパラメータとしてテンプレートを入力する必要があります。テンプレートは辞書オブジェクトになっており、その中でデータセンターやOSなどを指定していきます。
それぞれのキーと値については、[こちら](http://sldn.softlayer.com/reference/services/softlayer_hardware/createobject)に記載されています。一応下記に簡単に説明します。

| パラメータ | 概要 |データ型|
|:-:|:-:|:-:|
|datacenter.name|データセンターの指定|string|
|hostname|サーバーのホスト名|string|
|domain|サーバーのドメイン名|string|
|hourlyBillingFlag|Trueの場合時間課金のサーバーになります。|boolean|
|fixedConfigurationPreset.keyName|プリセットの種類を選択します。|string|
|networkComponents.maxSpeed|NICのポート速度を指定します。|int|
|privateNetworkOnlyFlag|Trueの場合、private側のNICのみでデプロイされます。|boolean|
|operatingSystemReferenceCode|OSを指定します。|string|

各パラメータで指定できる値については、HardwareサービスのgetCreateObjectOptionsメソッドを使用することで一覧が確認できます。
ちなみに時間課金のベアメタル・サーバーの場合のみprimaryNetworkComponent.networkVlan.idパラメータなどによるVLANの指定はできません。Bluemix側にチケットでお願いしてもできません。

最後のbms_allhostsにはベアメタル・サーバーのFQDNを格納しています。チケットの発行時に利用します。


##ベアメタル・サーバーの情報を取得

```py:handson.py        
    ##############################################################################################
    #Retrieve BMS information
    ##############################################################################################
    
    
    #Get BMS id from Global ID
    mask_1 = "mask[id;globalIdentifier]"
    new_bms_id = 0
    
    while True:
        bms_infor = client['Account'].getHardware(mask=mask_1)
        for bms_id in bms_infor:
            try:
                if bms_id['globalIdentifier'] == order_bms['globalIdentifier']:
                    new_bms_id = bms_id['id']
                    break
            except KeyError:
                print('please wait for a minutes... now provisioning BMS...')
                sleep(120)
        if new_bms_id > 0:
            break
    
    print(new_bms_id)
    
    #Get BMS private VLAN ID
    mask_2 = "mask[id]"
    bms_vlan_id = 0
    
    while True:
        bms_vlan = client['Hardware'].getNetworkVlans(id=new_bms_id,mask=mask_2)
        bms_vlan_id = bms_vlan[0]['id']
        if bms_vlan_id > 0:
            break


```

ベアメタル・サーバーを先ほどのcreateObjectで注文した際に、サーバーの情報はAPIの戻り値としてorder_bmsに格納されます。しかし、注文が確定した時点ではサーバーのidやVLANの情報などは確定していません。下記に戻り値の例を記載します。

```py:handson.py
{'datacenter': {'statusId': '', 'id': '', 'name': 'hkg02'}, 'domain': 'softlayer.com', 'hostname': 'handson.bms1', 'provisionDate': '', 'fixedConfigurationPreset': {'keyName': 'S1270_32GB_1X1TBSATA_NORAID', 'id': '', 'packageId': ''}, 'bareMetalInstanceFlag': '', 'privateNetworkOnlyFlag': True, 'hardwareStatusId': '', 'globalIdentifier': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'operatingSystemReferenceCode': 'CUSTOS_1_64', 'hourlyBillingFlag': True, 'serviceProviderResourceId': '', 'networkComponents': [{'maxSpeed': 1000, 'modifyDate': '', 'networkVlanId': '', 'hardwareId': '', 'id': '', 'speed': '', 'port': ''}], 'serviceProviderId': '', 'id': '', 'accountId': xxxxxxxx}
```

idや、networkVlanIdの値が空になっています。この段階ではまだプロビジョニングが完了していないため、これらの情報を取得することができません。

そこでオーダーの瞬間に確定するglobalIdentifierを利用してAccountサービスのgetHardwareメソッドからidを引っ張り出しています。プロビジョニングが終わっていない場合はしばらく待機するようにしています。

VLAN IDも同じようにAccountサービスのgetNetworkVlansから探すことが可能です。

ちなみにサーバーのidはこのあとアクセス権限を設定するために、VLAN IDは仮想サーバーにVLANを指定するためにそれぞれ利用します。


##時間課金仮想サーバーのオーダー

```py:handson.py
    ##############################################################################################
    #Order hourly VSI
    ##############################################################################################

    
    #Set VSI order template
    virtual_server = {
        'datacenter': {'name': 'hkg02'},
        'hostname': 'handson.vsi' + str(num+1),
        'domain': 'softlayer.com',
        'hourlyBillingFlag': True,
        'startCpus': 2,
        'maxMemory': 4096,
        'blockDeviceTemplateGroup':{
            'globalIdentifier': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
        },
        'localDiskFlag': False,
        'primaryBackendNetworkComponent':{
            'networkVlan':{
                'id': bms_vlan_id
            }
        },
        'networkComponents': [{
            'maxSpeed': 1000,
        }],
        'privateNetworkOnlyFlag': True,
    }
    
    #Order VSI
    try:
        order_vsi = client['Virtual_Guest'].createObject(virtual_server)
        print("complete order VSI!") 
    except SoftLayer.SoftLayerAPIError as e: 
        print("Error: %s, %s" % (e.faultCode, e.faultString)) 
        exit()
        
```

時間課金の仮想サーバーをオーダーします。ベアメタル・サーバーの時とほとんど同じような形でオーダーすることができます。Virtual_GuestサービスのcreateObjectメソッドを利用します。

ベアメタル・サーバーと違う点は、まずprimaryBackendNetworkComponent.networkVlanパラメーターでデプロイするVLANを指定しています。仮想サーバーは時間課金であってもVLANを選択することができるため、先ほどオーダーしたベアメタル・サーバーと同じVLAN上にデプロイしています。
また、blockDeviceTemplateGroupにイメージテンプレートのglobalIdentifierを指定することで、イメージからオーダーすることが可能です。この場合OSやディスクについては指定できません。

ちなみにイメージテンプレートでは公開と非公開が指定できますが、もちろんどちらからもオーダーできます。
公開の場合はVirtual_Guest_Block_Device_Template_GroupサービスのgetPublicImagesメソッドで、非公開の場合はAccountサービスのgetBlockDeviceTemplateGroupsメソッドからそれぞれ一覧を取得できます。


##仮想サーバーの情報を取得


```py:handson.py
    ##############################################################################################
    #Retrieve VSI information
    ##############################################################################################
    
    
    #Get VSI id from Global ID
    mask_3 = "mask[id;globalIdentifier]"
    new_vsi_id = 0

    while True:
        vsi_infor = client['Account'].getVirtualGuests(mask=mask_3)
        for vsi_id in vsi_infor:
            try:
                if vsi_id['globalIdentifier'] == order_vsi['globalIdentifier']:
                    new_vsi_id = vsi_id['id']
                    break
            except KeyError:
                print('please wait for a minutes... now provisioning VSI...')
                sleep(60)
        if new_vsi_id > 0:
            break
    
```

ベアメタル・サーバーの時と同様に仮想サーバーのidを取得します。


##サーバーへのアクセス権限を追加


```py:handson.py    
    ##############################################################################################
    #Server access control 
    ##############################################################################################    
    
    
    
    #Remove all access
    client['User_Customer'].removeAllHardwareAccessForThisUser(id=new_user['id'])
    client['User_Customer'].removeAllVirtualAccessForThisUser(id=new_user['id'])
    
    #Create lists of new server id
    new_bms_idset = [new_bms_id]
    new_vsi_idset = [new_vsi_id]
    
    #Add specific server access
    client['User_Customer'].addBulkHardwareAccess(new_bms_idset,id=new_user['id'])    
    client['User_Customer'].addBulkVirtualGuestAccess(new_vsi_idset,id=new_user['id'])
    
    
f.close()
```

作成したユーザーへ、サーバーのアクセス権限を付与します。
最初にUser_CustomerサービスのremoveAllHardwareAccessForThisUserメソッド、removeAllVirtualAccessForThisUserメソッドを利用し、全サーバーへのアクセス権限を剥奪します。

そのあと、先ほど作成したサーバーへのアクセス権限をaddBulkHardwareAccessメソッドとaddBulkVirtualGuestAccessメソッドを使い付与します。

注意点として、addBulkHardwareAccess、addBulkVirtualGuestAccessに[代入するパラメータ](http://sldn.softlayer.com/reference/services/softlayer_user_customer/addbulkhardwareaccess)の中の「hardwareIds」と「virtualGuestIds」はint型ではなく配列ですので、リスト形式にするのを忘れないようにしてください。


##チケットの発行


```py:handson.py
##############################################################################################
#Issue ticket 
##############################################################################################

#Create ticket template
ticket_template = {
    'subjectId': 1021, # Hardware Issue
    'assignedUserId': userid,
    'title': 'IPMI privilege and UEFI mode'
}
ticket_contents = 'I have three requests. I would like to boot server from ISO and install vSphere(BYOL).\nI want to change the setting of following ' + str(counter) + ' servers.\n\n' + bms_allhosts + '1. Could you change the privilege of IPMI control from Operator to Administrator?\n2. Please raise a start priority of ISO.\n3. Please change the IPMI\'s UEFI mode into legacy only mode.\n\nI agree to enforcement if you need to reboot the servers'

#Issue ticket
created_ticket = client['Ticket'].createStandardTicket(ticket_template, ticket_contents)

```

最後にチケットを発行して終了です。TicketサービスのcreateStandardTicketメソッドを利用します。
テンプレートの中で指定するsubjectIdはTicket_SubjectサービスのgetAllObjectsメソッドで[一覧を取得](https://github.com/yukitada/softlayerapi/blob/master/Subjects)することができます。

また、ここで全てのサーバーを同時に設定変更してもらうように、ホストの一覧をbms_allhostsに格納してあるので、それを利用しています。


#まとめ

今回は時間のかかるハンズオンの環境作成をSoftLayer APIで自動化しました。もちろんハンズオンの環境だけでなく、サーバーを10台オーダーしたり、チケットを何回も発行したり、様々な管理が自動化できるのは非常に魅力的だと思います。

皆さんも是非ご利用ください！！

また、VMware on IBM Cloudのハンズオンは毎月行われているので、Bluemixを触ってみたい人、VMwareが大好きな人など、是非是非ご利用ください。無償です。
[vSphere "6.5" on IBM Bluemix Infrastructure(SoftLayer) ハンズオン](https://www.ibm.com/cloud-computing/jp/ja/community/cloud-service.html)