/*
* Copyright 2016 by PrimeCloud Controller/OSS Community.
*
* This file is part of PrimeCloud Controller(TM).
*
* PrimeCloud Controller(TM) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* PrimeCloud Controller(TM) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PrimeCloud Controller(TM). If not, see <http://www.gnu.org/licenses/>.
*/
package jp.primecloud.auto.process.aws;
import java.util.Date;
import java.util.List;
import jp.primecloud.auto.entity.crud.AwsAddress;
import jp.primecloud.auto.entity.crud.AwsInstance;
import jp.primecloud.auto.entity.crud.Instance;
import jp.primecloud.auto.exception.AutoApplicationException;
import jp.primecloud.auto.exception.AutoException;
import jp.primecloud.auto.process.ProcessLogger;
import jp.primecloud.auto.service.ServiceSupport;
import jp.primecloud.auto.util.MessageUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.time.DateFormatUtils;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.ec2.model.Address;
import com.amazonaws.services.ec2.model.AllocateAddressRequest;
import com.amazonaws.services.ec2.model.AllocateAddressResult;
import com.amazonaws.services.ec2.model.AssociateAddressRequest;
import com.amazonaws.services.ec2.model.DisassociateAddressRequest;
import com.amazonaws.services.ec2.model.DomainType;
import com.amazonaws.services.ec2.model.ReleaseAddressRequest;
/**
* <p>
* TODO: クラスコメント
* </p>
*
*/
public class AwsAddressProcess extends ServiceSupport {
protected AwsCommonProcess awsCommonProcess;
protected ProcessLogger processLogger;
/**
* TODO: メソッドコメント
*
* @param awsProcessClient
* @return
*/
public AwsAddress createAddress(AwsProcessClient awsProcessClient) {
// Elastic IPの確保
AllocateAddressRequest request = new AllocateAddressRequest();
if (BooleanUtils.isTrue(awsProcessClient.getPlatformAws().getVpc())) {
request.withDomain(DomainType.Vpc);
}
String publicIp;
try {
AllocateAddressResult result = awsProcessClient.getEc2Client().allocateAddress(request);
publicIp = result.getPublicIp();
} catch (AutoException e) {
// Elastic IPの上限オーバーの場合
if (e.getCause() instanceof AmazonServiceException
&& "AddressLimitExceeded".equals(((AmazonServiceException) e.getCause()).getErrorCode())) {
throw new AutoApplicationException("EPROCESS-000134");
}
throw e;
}
// イベントログ出力
processLogger.debug(null, null, "AwsElasticIpAllocate", new Object[] {
awsProcessClient.getPlatform().getPlatformName(), publicIp });
// AWSアドレス情報を作成
AwsAddress awsAddress = new AwsAddress();
awsAddress.setUserNo(awsProcessClient.getUserNo());
awsAddress.setPlatformNo(awsProcessClient.getPlatform().getPlatformNo());
awsAddress.setPublicIp(publicIp);
awsAddress.setComment("Allocate at " + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
awsAddressDao.create(awsAddress);
return awsAddress;
}
/**
* TODO: メソッドコメント
*
* @param awsProcessClient
* @param addressNo
*/
public void deleteAddress(AwsProcessClient awsProcessClient, Long addressNo) {
// AWSアドレス情報の存在チェック
AwsAddress awsAddress = awsAddressDao.read(addressNo);
if (awsAddress == null) {
return;
}
// Elastic IPのチェック
if (StringUtils.isEmpty(awsAddress.getPublicIp())) {
// Elastic IPが空ならAWSアドレス情報を削除して終了
awsAddressDao.delete(awsAddress);
return;
}
// Elastic IPを解放
try {
ReleaseAddressRequest request = new ReleaseAddressRequest();
// VPCの場合
if (BooleanUtils.isTrue(awsProcessClient.getPlatformAws().getVpc())) {
// 割り当てIDを取得する
Address address = awsCommonProcess.describeAddress(awsProcessClient, awsAddress.getPublicIp());
request.withAllocationId(address.getAllocationId());
}
// 非VPCの場合
else {
request.withPublicIp(awsAddress.getPublicIp());
}
awsProcessClient.getEc2Client().releaseAddress(request);
// イベントログ出力
processLogger.debug(null, null, "AwsElasticIpRelease", new Object[] {
awsProcessClient.getPlatform().getPlatformName(), awsAddress.getPublicIp() });
} catch (Exception ignore) {
// Elastic IPが実際には存在しない場合などに備えて、警告ログを出力して例外を握りつぶす
log.warn(ignore.getMessage());
}
// AWSアドレス情報を削除
awsAddressDao.delete(awsAddress);
}
/**
* TODO: メソッドコメント
*
* @param awsProcessClient
* @param instanceNo
*/
public void startAddress(AwsProcessClient awsProcessClient, Long instanceNo) {
// アドレス情報の取得
List<AwsAddress> awsAddresses = awsAddressDao.readByInstanceNo(instanceNo);
if (awsAddresses.size() > 1) {
// アドレス情報が複数ある場合
AutoException exception = new AutoException("EPROCESS-000202", instanceNo);
exception.addDetailInfo("result=" + awsAddresses);
throw exception;
}
if (awsAddresses.isEmpty()) {
// アドレス情報がない場合は終了
return;
}
AwsAddress awsAddress = awsAddresses.get(0);
// インスタンスIDがない場合、インスタンスに関連付ける
if (StringUtils.isEmpty(awsAddress.getInstanceId())) {
// アドレスのステータスチェック
Address address = checkAvailableAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo());
// アドレスの関連付け
associateAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo(), address);
// インスタンスのアドレス情報を更新
updateInstanceAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo());
}
}
/**
* TODO: メソッドコメント
*
* @param awsProcessClient
* @param instanceNo
*/
public void stopAddress(AwsProcessClient awsProcessClient, Long instanceNo) {
// アドレス情報の取得
List<AwsAddress> awsAddresses = awsAddressDao.readByInstanceNo(instanceNo);
if (awsAddresses.size() > 1) {
// アドレス情報が複数ある場合
AutoException exception = new AutoException("EPROCESS-000202", instanceNo);
exception.addDetailInfo("result=" + awsAddresses);
throw exception;
}
if (awsAddresses.isEmpty()) {
// アドレス情報がない場合は終了
return;
}
AwsAddress awsAddress = awsAddresses.get(0);
// インスタンスIDがある場合、インスタンスから切り離す
if (StringUtils.isNotEmpty(awsAddress.getInstanceId())) {
try {
// アドレスのステータスチェック
Address address = checkAssociatedAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo());
// アドレスの切り離し
disassociateAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo(), address);
// インスタンスのアドレス情報を更新
updateInstanceAddress(awsProcessClient, instanceNo, awsAddress.getAddressNo());
} catch (AutoException ignore) {
// 情報が不整合(インスタンス異常終了時など)の場合、警告ログと後始末のみ行う
log.warn(ignore.getMessage());
awsAddress = awsAddressDao.read(awsAddress.getAddressNo());
awsAddress.setInstanceId(null);
awsAddressDao.update(awsAddress);
}
}
}
public Address checkAvailableAddress(AwsProcessClient awsProcessClient, Long instanceNo, Long addressNo) {
AwsAddress awsAddress = awsAddressDao.read(addressNo);
String publicIp = awsAddress.getPublicIp();
Address address = awsCommonProcess.describeAddress(awsProcessClient, publicIp);
if (!StringUtils.isEmpty(address.getInstanceId())) {
// アドレスが何らかのインスタンスに関連付けられている場合
throw new AutoException("EPROCESS-000119", publicIp, address.getInstanceId());
}
return address;
}
public void associateAddress(AwsProcessClient awsProcessClient, Long instanceNo, Long addressNo, Address address) {
AwsAddress awsAddress = awsAddressDao.read(addressNo);
AwsInstance awsInstance = awsInstanceDao.read(instanceNo);
// アドレスの関連付け
AssociateAddressRequest request = new AssociateAddressRequest();
request.withInstanceId(awsInstance.getInstanceId());
// VPCの場合
if (BooleanUtils.isTrue(awsProcessClient.getPlatformAws().getVpc())) {
// 割り当てIDを指定する
request.withAllocationId(address.getAllocationId());
}
// 非VPCの場合
else {
request.withPublicIp(awsAddress.getPublicIp());
}
awsProcessClient.getEc2Client().associateAddress(request);
// ログ出力
if (log.isInfoEnabled()) {
log.info(MessageUtils.getMessage("IPROCESS-100131", awsAddress.getPublicIp(), awsInstance.getInstanceId()));
}
// イベントログ出力
Instance instance2 = instanceDao.read(instanceNo);
processLogger.debug(null, instance2, "AwsElasticIpAssociate", new Object[] { awsInstance.getInstanceId(),
awsAddress.getPublicIp() });
// データベースの更新
awsAddress.setInstanceId(awsInstance.getInstanceId());
awsAddressDao.update(awsAddress);
}
public void updateInstanceAddress(AwsProcessClient awsProcessClient, Long instanceNo, Long addressNo) {
AwsAddress awsAddress = awsAddressDao.read(addressNo);
AwsInstance awsInstance = awsInstanceDao.read(instanceNo);
// 最新のインスタンスを取得
com.amazonaws.services.ec2.model.Instance instance = null;
long timeout = 180 * 1000L;
long startTime = System.currentTimeMillis();
while (true) {
try {
Thread.sleep(1000L * awsProcessClient.getDescribeInterval());
} catch (InterruptedException ignore) {
}
instance = awsCommonProcess.describeInstance(awsProcessClient, awsInstance.getInstanceId());
if (!StringUtils.equals(instance.getPublicDnsName(), awsInstance.getDnsName())
&& !StringUtils.equals(instance.getPublicDnsName(), instance.getPrivateDnsName())) {
// DnsNameが変更されており、PrivateDnsNameと違っていれば、最新インスタンス情報を取得できたとみなす(非VPC環境用)
break;
}
// アドレスを関連付けた場合
if (StringUtils.isNotEmpty(awsInstance.getInstanceId())) {
if (StringUtils.equals(instance.getPublicIpAddress(), awsAddress.getPublicIp())) {
// PublicIPが変更されており、ElasticIPと同じであれば、最新インスタンス情報を取得できたとみなす
break;
}
}
// アドレスを切り離した場合
else {
if (!StringUtils.equals(instance.getPublicIpAddress(), awsAddress.getPublicIp())) {
// PublicIPが変更されており、ElasticIPと違っていれば、最新インスタンス情報を取得できたとみなす
break;
}
}
if (System.currentTimeMillis() - startTime > timeout) {
// タイムアウト発生時
AutoException exception = new AutoException("EPROCESS-000204", awsInstance.getInstanceId());
exception.addDetailInfo("result=" + ReflectionToStringBuilder.toString(instance));
throw exception;
}
}
// データベースの更新
awsInstance.setDnsName(instance.getPublicDnsName());
awsInstance.setPrivateDnsName(instance.getPrivateDnsName());
awsInstance.setIpAddress(instance.getPublicIpAddress());
awsInstance.setPrivateIpAddress(instance.getPrivateIpAddress());
awsInstanceDao.update(awsInstance);
}
public Address checkAssociatedAddress(AwsProcessClient awsProcessClient, Long instanceNo, Long addressNo) {
AwsAddress awsAddress = awsAddressDao.read(addressNo);
String publicIp = awsAddress.getPublicIp();
String instanceId = awsAddress.getInstanceId();
// アドレスが関連付けられているかどうかのチェック
Address address = awsCommonProcess.describeAddress(awsProcessClient, publicIp);
if (StringUtils.isEmpty(address.getInstanceId())) {
// アドレスがどのインスタンスにも関連付けられていない場合
throw new AutoException("EPROCESS-000120", publicIp, instanceId);
} else if (!StringUtils.equals(instanceId, address.getInstanceId())) {
// アドレスが他インスタンスに関連付けられている場合
throw new AutoException("EPROCESS-000121", publicIp, instanceId, address.getInstanceId());
}
return address;
}
public void disassociateAddress(AwsProcessClient awsProcessClient, Long instanceNo, Long addressNo, Address address) {
AwsAddress awsAddress = awsAddressDao.read(addressNo);
// アドレスの切り離し
DisassociateAddressRequest request = new DisassociateAddressRequest();
// VPCの場合
if (BooleanUtils.isTrue(awsProcessClient.getPlatformAws().getVpc())) {
// 関連付けIDを指定する
request.withAssociationId(address.getAssociationId());
}
// 非VPCの場合
else {
request.withPublicIp(awsAddress.getPublicIp());
}
awsProcessClient.getEc2Client().disassociateAddress(request);
// ログ出力
if (log.isInfoEnabled()) {
log.info(MessageUtils.getMessage("IPROCESS-100132", awsAddress.getPublicIp(), awsAddress.getInstanceId()));
}
//イベントログ出力
Instance instance = instanceDao.read(instanceNo);
processLogger.debug(null, instance, "AwsElasticIpDisassociate", new Object[] { awsAddress.getInstanceId(),
awsAddress.getPublicIp() });
// データベースの更新
awsAddress.setInstanceId(null);
awsAddressDao.update(awsAddress);
}
public void setAwsCommonProcess(AwsCommonProcess awsCommonProcess) {
this.awsCommonProcess = awsCommonProcess;
}
public void setProcessLogger(ProcessLogger processLogger) {
this.processLogger = processLogger;
}
}