softlayer-java apiを使ってsoftlayerを触ってみる

# はじめに

SoftLayerといえばPythonのAPIが活発ですが、あえてJavaを使って試してみました。
環境設定、仮想サーバ・ベアメタルインスタンスの情報表示方法、電源ON/OFFの仕方、フレックスイメージの作成方法なんかを見ていきます。物理サーバもAPIで操作できちゃうのが私みたいな田舎者には新鮮です。

文中に出てくるJavaのコードはjava8のstream APIを使っていますので、慣れてない方は合わせて勉強しておくと良いと思います。

今後も使えそうなネタがあったら随時更新していきます。

# 環境

* softlayer-java 0.2.2
* Java8
* Apache Maven 3.0.4

# 情報源

残念ながら日本語の情報は皆無なのでソースとAPIリファレンスを見ながら進めていくしかありません。こちらが参考になりました。

* [softlayer-java@github](https://github.com/softlayer/softlayer-java)
* [APIリファレンス](http://sldn.softlayer.com/reference/services/SoftLayer_Account)

# 環境構築

1. mavenのセントラルリポジトリにSoftlayerのライブラリは登録されていますのでpomに依存関係を追加するだけで設定は完了します。
SoftLayerのAPIから取得できる値は非常に多く、オブジェクトに入っているデータをまとめて見たかったりするので、commons-langのToStringBuilderを使いたかったのでcommons-langも合わせて依存関係に追加しています。ここはお好みで。

```(抜粋)pom.xml
	<dependencies>
		<dependency>
			<groupId>com.softlayer.api</groupId>
			<artifactId>softlayer-api-client</artifactId>
			<version>0.2.2</version>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.3</version>
		</dependency>
	</dependencies>
```

２．APIキーを発行します。

Account - Usersの画面でAPIKeyという項目があるのでここで発行します。
IDはログイン時のIDになるので実際に発行されるのはAPIKeyのみとなります。

これでAPIを実行できる準備ができました。

# 作る前に・・・・

すこし作り始めてみると判るのですが色々疑問が湧きます。

* ID/PWはソースにベタ書きなのか？
* プロキシ配下でAPI叩くにはどうすればよいのか？
* 通信のログはどのように出すのか？

## ID/PWはソースにベタ書き？

ベタ書きです。AWSのようにEnvironmentVariableCredentialsProvider, SystemPropertiesCredentialsProvider, InstanceProfileCredentialsProviderなどのような便利なものはありません。また、STSのような仕組みもありませんので大事に管理しましょう。

## プロキシ配下でSoftLayerAPIを叩くにはどうするの？

Githubに以下のような記述があります。

```
HTTP Client
The default HTTP client that is used is the JVM's native HttpUrlConnection. In order to create your own, alternative implementation you must implement com.softlayer.api.http.HttpClientFactory. Once implemented, this can be explicitly set on the RestApiClient by calling setHttpClientFactory. Instead of setting the factory manually, you can also leverage Java's ServiceLoader mechanism to have it used by default. This involves adding the fully qualified class name of your implementation on a single line in a file in the JAR at META-INF/com.softlayer.api.http.HttpClientFactory.
```

要はHttpUrlConnectionを内部で使っていてFactoryもあるのでそこに差し込めや。と。ちょっと見てみたのですがちょっと面倒だったので後回しにしました。単にプロキシ経由で通信したいだけであれば、HttpUrlConnectionはシステムプロパティにプロキシの設定をしてあげるとそれを読んでくれますのでそれを使いましょう。

```
System.setProperty("proxySet", "true");
System.setProperty("proxyHost", proxyHost);
System.setProperty("proxyPort", proxyPort);
```

## 通信のログはどのように出すのか？

これもGithubに答えが載ってます。ApiClientに対してwithLoggingEnabled()を呼べだそうです。

```
Logging

Logging the requests and response to stdout can be enabled by invoking withLoggingEnabled on the RestApiClient. In order to log elsewhere, simply make your own implementation of RestApiClient with logRequest and logResponse overridden.
```

## SoftLayerAPIClientを簡単に作れるようにしておく。

毎回アクセスキー設定したり、ログ出す？出さない？とかプロキシON/OFFとか実行する旅に変えるのは大変なので設定ファイルに纏めて外出ししておきます。全く大したことないコードなので解説は省きます。改変などはご自由にどうぞ。

[SoftLayerClientProvider.java](https://github.com/uzresk/softlayer/blob/master/src/main/java/provider/SoftLayerClientProvider.java)

### 利用方法

１．クラスパスルート直下にsoftlayer.propertiesを作成し下記を設定します。

```
softlayer.username=xxxxx
softlayer.apikey=xxxxxxxxxxxxxx
proxyHost=proxy.intra.co.jp
proxyPort=8080
log.enable=0
```

２．ApiClientを取得します。

``` get.java
ApiClient client = SoftLayerClientProvider.createApiClient();
```

これで本当に準備ができました。

# 作ってみる

あとはAPIの逆引きリファレンスな感じでさらっと説明します。

## VirtualServer

### VirtualServerの情報をすべて取得する。

```DescribeVirtualInstances.java
	private static void describeInstance() {

		ApiClient client = SoftLayerClientProvider.createApiClient();
		Account.Service accountService = Account.service(client);
		accountService
				.getVirtualGuests()
				.stream()
				.map(s -> "[" + s.getId() + "][" + s.getFullyQualifiedDomainName() + "]["
						+ s.asService(client).getPowerState().getName() + "]")
				.forEach(System.out::println);
	}
```

map(s->の部分で出力する情報を制御しています。本当に沢山のプロパティが取得できます。ToStringBuilderでまとめて出した結果はこちらです。

```virtual.log
com.softlayer.api.service.virtual.Guest@f5acb9d[
  account=<null>
  accountOwnedPoolFlag=<null>
  activeNetworkMonitorIncident=<null>
  activeTickets=<null>
  activeTransaction=<null>
  activeTransactions=<null>
  allowedHost=<null>
  allowedNetworkStorage=<null>
  antivirusSpywareSoftwareComponent=<null>
  applicationDeliveryController=<null>
  attributes=<null>
  availableMonitoring=<null>
  averageDailyPrivateBandwidthUsage=<null>
  averageDailyPublicBandwidthUsage=<null>
  backendNetworkComponents=<null>
  backendRouters=<null>
  bandwidthAllocation=<null>
  bandwidthAllotmentDetail=<null>
  billingCycleBandwidthUsage=<null>
  billingCyclePrivateBandwidthUsage=<null>
  billingCyclePublicBandwidthUsage=<null>
  billingItem=<null>
  blockCancelBecauseDisconnectedFlag=<null>
長すぎるので以下略
]
```

### 特定のVirtualServerの情報を取得する

インスタンスIDを引数に実行します。

```DescribeVirtualInstances.java
	private static void describeInstance(Long id) {
		ApiClient client = SoftLayerClientProvider.createApiClient();
		Account.Service accountService = Account.service(client);
		System.out.println("[ID][Domain][Status]");
		accountService
				.getVirtualGuests()
				.stream()
				.filter(s -> id.equals(s.getId()))
				.map(s -> "[" + s.getId() + "]["
						+ s.getFullyQualifiedDomainName() + "]["
						+ s.asService(client).getPowerState().getName() + "]")
				.forEach(System.out::println);
	}
```

### 特定のVirtualServerの電源ON/OFF

Running(起動中）の場合は停止します。Halted(停止中)の場合は起動します。

```PowerOnOffVirtualInstance.java
		Stream<Guest> stream = accountService.getVirtualGuests().stream()
				.filter(s -> id.equals(s.getId()));
		Guest guest = stream.findFirst().get();

		String powerStatus = guest.asService(client).getPowerState().getName();

		System.out.println("id[" + id + "] Status[" + powerStatus + "]");

		if ("Running".equals(powerStatus)) {
			guest.asService(client).powerOffSoft();

		} else if ("Halted".equals(powerStatus)) {
			guest.asService(client).powerOn();
		}

```

## Baremetal

### BaremetalServerの一覧を取得する。

accountService.withMask()で取得する情報をマスクしてあげます。マスクしないとデータの返却に非常に時間が掛かる or タイムアウトします。
仮想サーバでもマスクすることはできますが、こちらはマスクしなくても問題なく動きました。

```DescribeBaremetalInstances.java
	private static void describeBaremetalInstances() {

		ApiClient client = SoftLayerClientProvider.createApiClient();
		Account.Service accountService = Account.service(client);
		accountService.withMask().hardware().id();
		accountService.withMask().hardware().hostname();
		accountService.withMask().hardware().fullyQualifiedDomainName();

		accountService
				.getObject()
				.getHardware()
				.stream()
				.map(s -> "[" + s.getId() + "]["
						+ s.getFullyQualifiedDomainName() + "]["
						+ s.asService(client).getServerPowerState() + "]")
				.forEach(System.out::println);
	}
```

### 特定のBaremetalの情報を取得する。

``` DescribeBaremetalInstances.java
	private static void describeBaremetalInstance(Long id) {

		ApiClient client = SoftLayerClientProvider.createApiClient();

		Account.Service accountService = Account.service(client);
		accountService.withMask().hardware().id();
		accountService.withMask().hardware().hostname();
		accountService.withMask().hardware().fullyQualifiedDomainName();

		accountService
				.getObject()
				.getHardware()
				.stream()
				.filter(s -> id.equals(s.getId()))
				.map(s -> "[" + s.getId() + "]["
						+ s.getFullyQualifiedDomainName() + "]["
						+ s.asService(client).getServerPowerState() + "]")
				.forEach(System.out::println);
	}
```

### 特定のBaremetalの電源ON/OFF

idをつかってHardwareを特定して電源ON/OFFをしています。

```PowerOnOffBaremetalInstance.java
		Stream<Hardware> stream = accountService.getObject().getHardware()
				.stream().filter(s -> id.equals(s.getId()));
		Hardware hardware = stream.findFirst().get();

		String powerStatus = hardware.asService(client).getServerPowerState();

		System.out.println("id[" + id + "] Status[" + powerStatus + "]");

		if ("on".equals(powerStatus)) {
			hardware.asService(client).powerOff();

		} else if ("off".equals(powerStatus)) {
			hardware.asService(client).powerOn();
		}
```

## イメージテンプレート

### イメージテンプレートの一覧を取得する。

```DescribeImages.java
	private static void describeImages() {
		ApiClient client = SoftLayerClientProvider.createApiClient();
		Account.Service accountService = Account.service(client);
		accountService
				.getBlockDeviceTemplateGroups()
				.stream()
				.map(s -> "[" + s.getName() + "]["
						+ new Date(s.getCreateDate().getTimeInMillis()) + "]["
						+ s.getGlobalIdentifier() + "]")
				.forEach(System.out::println);
	}
```

### 特定のイメージテンプレートの情報を取得する

```DescribeImages.java
	private static void describeImage(Long id) {
		ApiClient client = SoftLayerClientProvider.createApiClient();
		Account.Service accountService = Account.service(client);

		// describe image from imageId
		accountService
				.getBlockDeviceTemplateGroups()
				.stream()
				.filter(s -> id.equals(s.getId()))
				.map(s -> ToStringBuilder.reflectionToString(s,
						ToStringStyle.MULTI_LINE_STYLE))
				.forEach(System.out::println);

	}
```

### フレックスイメージを取得する。

APIを使ってフレックスイメージも取得できます。テンプレートの名前とかをきめてcaptureImageを呼ぶだけですね。

```CreateFlexImageTemplate.java
		ApiClient client = SoftLayerClientProvider.createApiClient();

		Account.Service accountService = Account.service(client);

		final String deviceName = args[0];

		// template description
		Template paramTemplate = new Template();
		paramTemplate.setName("template from " + deviceName);
		paramTemplate.setDescription("description example");
		paramTemplate.setSummary("summary example");

		// create flex image
		Stream<Group> stream = accountService
				.getVirtualGuests()
				.stream()
				.filter(s -> deviceName.equals(s.getFullyQualifiedDomainName()))
				.map(s -> s.asService(client).captureImage(paramTemplate));

		// describe template
		System.out.println(ToStringBuilder.reflectionToString(stream
				.findFirst().get(), ToStringStyle.MULTI_LINE_STYLE));

```