/* * 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.ArrayList; import java.util.List; import jp.primecloud.auto.config.Config; import jp.primecloud.auto.entity.crud.AwsInstance; import jp.primecloud.auto.entity.crud.AwsVolume; import jp.primecloud.auto.entity.crud.Component; import jp.primecloud.auto.entity.crud.Farm; import jp.primecloud.auto.entity.crud.Instance; import jp.primecloud.auto.entity.crud.PlatformAws; import jp.primecloud.auto.entity.crud.User; 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 com.amazonaws.services.ec2.model.AttachVolumeRequest; import com.amazonaws.services.ec2.model.AttachVolumeResult; import com.amazonaws.services.ec2.model.CreateVolumeRequest; import com.amazonaws.services.ec2.model.CreateVolumeResult; import com.amazonaws.services.ec2.model.DeleteVolumeRequest; import com.amazonaws.services.ec2.model.DetachVolumeRequest; import com.amazonaws.services.ec2.model.DetachVolumeResult; import com.amazonaws.services.ec2.model.Tag; import com.amazonaws.services.ec2.model.Volume; import com.amazonaws.services.ec2.model.VolumeAttachment; import com.amazonaws.services.ec2.model.VolumeState; /** * <p> * TODO: クラスコメントを記述 * </p> * */ public class AwsVolumeProcess extends ServiceSupport { protected AwsCommonProcess awsCommonProcess; protected ProcessLogger processLogger; /** * TODO: メソッドコメント * * @param awsProcessClient * @param instanceNo */ public void startVolume(AwsProcessClient awsProcessClient, Long instanceNo) { // ボリューム情報の取得 List<AwsVolume> awsVolumes = awsVolumeDao.readByInstanceNo(instanceNo); if (awsVolumes.isEmpty()) { return; } for (AwsVolume awsVolume : awsVolumes) { startVolume(awsProcessClient, instanceNo, awsVolume.getVolumeNo()); } } /** * TODO: メソッドコメント * * @param awsProcessClient * @param instanceNo * @param volumeNo */ public void startVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); // ボリュームIDがない場合、ボリュームを作成する if (StringUtils.isEmpty(awsVolume.getVolumeId())) { // ボリュームの作成 createVolume(awsProcessClient, instanceNo, volumeNo); // ボリュームの作成待ち waitCreateVolume(awsProcessClient, instanceNo, volumeNo); // ボリュームにタグを付ける createTag(awsProcessClient, volumeNo); } // インスタンスIDがない場合、インスタンスにアタッチする if (StringUtils.isEmpty(awsVolume.getInstanceId())) { // ボリュームのアタッチ attachVolume(awsProcessClient, instanceNo, volumeNo); // ボリュームのアタッチ待ち waitAttachVolume(awsProcessClient, instanceNo, volumeNo); } } /** * TODO: メソッドコメント * * @param awsProcessClient * @param instanceNo */ public void stopVolume(AwsProcessClient awsProcessClient, Long instanceNo) { // ボリューム情報の取得 List<AwsVolume> awsVolumes = awsVolumeDao.readByInstanceNo(instanceNo); if (awsVolumes.isEmpty()) { return; } for (AwsVolume awsVolume : awsVolumes) { stopVolume(awsProcessClient, instanceNo, awsVolume.getVolumeNo()); } } /** * TODO: メソッドコメント * * @param awsProcessClient * @param instanceNo * @param volumeNo */ public void stopVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); // インスタンスIDがある場合、インスタンスからデタッチする if (StringUtils.isNotEmpty(awsVolume.getInstanceId())) { try { // ボリュームのデタッチ detachVolume(awsProcessClient, instanceNo, volumeNo); // ボリュームのデタッチ待ち waitDetachVolume(awsProcessClient, instanceNo, volumeNo); } catch (AutoException ignore) { // 情報が不整合(インスタンス異常終了時など)の場合、警告ログと後始末のみ行う log.warn(ignore.getMessage()); awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(VolumeState.Error.toString()); awsVolume.setInstanceId(null); awsVolumeDao.update(awsVolume); } } } public void createVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); AwsInstance awsInstance = awsInstanceDao.read(instanceNo); // ボリュームの作成 CreateVolumeRequest request = new CreateVolumeRequest(); request.withSize(awsVolume.getSize()); request.withSnapshotId(awsVolume.getSnapshotId()); request.withAvailabilityZone(awsInstance.getAvailabilityZone()); String volumeType = Config.getProperty("aws.volumeType"); if (StringUtils.isNotEmpty(volumeType)) { request.withVolumeType(volumeType); } CreateVolumeResult result = awsProcessClient.getEc2Client().createVolume(request); Volume volume = result.getVolume(); String volumeId = volume.getVolumeId(); // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100121", volumeId)); } //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsCreate", new Object[] { awsProcessClient.getPlatform() .getPlatformName() }); // データベース更新 awsVolume.setVolumeId(volume.getVolumeId()); awsVolume.setStatus(volume.getState()); awsVolumeDao.update(awsVolume); } public void waitCreateVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); // ボリュームの作成待ち Volume volume; try { volume = awsCommonProcess.waitVolume(awsProcessClient, volumeId); if (!StringUtils.equals(volume.getState(), VolumeState.Available.toString())) { // ボリューム作成失敗時 AutoException exception = new AutoException("EPROCESS-000113", volumeId, volume.getState()); exception.addDetailInfo("result=" + ReflectionToStringBuilder.toString(volume)); throw exception; } // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100122", volumeId)); } } catch (AutoException e) { // ボリューム作成失敗時 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setVolumeId(null); awsVolume.setStatus(null); awsVolumeDao.update(awsVolume); throw e; } //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsCreateFinish", new Object[] { awsProcessClient.getPlatform().getPlatformName(), awsVolume.getVolumeId(), awsVolume.getSize() }); // データベース更新 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(volume.getState()); awsVolumeDao.update(awsVolume); } public void attachVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsInstance awsInstance = awsInstanceDao.read(instanceNo); AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsAttach", new Object[] { instance.getInstanceName(), awsVolume.getVolumeId(), awsVolume.getDevice() }); // ボリュームのアタッチ AttachVolumeRequest request = new AttachVolumeRequest(); request.withVolumeId(volumeId); request.withInstanceId(awsInstance.getInstanceId()); request.withDevice(awsVolume.getDevice()); AttachVolumeResult result = awsProcessClient.getEc2Client().attachVolume(request); VolumeAttachment attachment = result.getAttachment(); // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100123", volumeId, attachment.getInstanceId())); } // データベースの更新 awsVolume.setInstanceId(attachment.getInstanceId()); awsVolumeDao.update(awsVolume); } public void waitAttachVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); Volume volume = null; try { // volume = awsProcessClient.waitAttachVolume(volumeId, instanceId); // TODO: アタッチ情報がすぐに更新されない問題に暫定的に対応 int retry = 6; for (int i = 0; i < retry; i++) { volume = awsCommonProcess.waitVolume(awsProcessClient, volumeId); if (StringUtils.equals(volume.getState(), VolumeState.InUse.toString())) { break; } } if (!StringUtils.equals(volume.getState(), VolumeState.InUse.toString())) { // アタッチに失敗した場合 AutoException exception = new AutoException("EPROCESS-000115", awsVolume.getInstanceId(), volumeId, volume.getState()); exception.addDetailInfo("result=" + ReflectionToStringBuilder.toString(volume)); throw exception; } // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100124", volumeId, awsVolume.getInstanceId())); } } catch (AutoException e) { // アタッチに失敗した場合 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(VolumeState.Error.toString()); awsVolume.setInstanceId(null); awsVolumeDao.update(awsVolume); throw e; } //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsAttachFinish", new Object[] { instance.getInstanceName(), awsVolume.getVolumeId(), awsVolume.getDevice() }); // データベースの更新 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(volume.getState()); awsVolumeDao.update(awsVolume); } public void detachVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsDetach", new Object[] { instance.getInstanceName(), awsVolume.getVolumeId(), awsVolume.getDevice() }); // ボリュームのデタッチ DetachVolumeRequest request = new DetachVolumeRequest(); request.withVolumeId(volumeId); request.withInstanceId(awsVolume.getInstanceId()); request.withDevice(awsVolume.getDevice()); DetachVolumeResult result = awsProcessClient.getEc2Client().detachVolume(request); VolumeAttachment attachment = result.getAttachment(); // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100125", volumeId, attachment.getInstanceId())); } } public void waitDetachVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); String instanceId = awsVolume.getInstanceId(); Volume volume = null; try { // TODO: アタッチ情報がすぐに更新されない問題に暫定的に対応 int retry = 6; for (int i = 0; i < retry; i++) { volume = awsCommonProcess.waitVolume(awsProcessClient, volumeId); if (StringUtils.equals(volume.getState(), VolumeState.Available.toString())) { break; } } if (!StringUtils.equals(volume.getState(), VolumeState.Available.toString())) { // デタッチに失敗した場合 AutoException exception = new AutoException("EPROCESS-000116", instanceId, volumeId, volume.getState()); exception.addDetailInfo("result=" + ReflectionToStringBuilder.toString(volume)); throw exception; } // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100126", volumeId, instanceId)); } } catch (AutoException e) { // デタッチに失敗した場合 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(VolumeState.Error.toString()); awsVolume.setInstanceId(null); awsVolumeDao.update(awsVolume); throw e; } //イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = instanceDao.read(instanceNo); processLogger.debug(component, instance, "AwsEbsDetachFinish", new Object[] { instance.getInstanceName(), awsVolume.getVolumeId(), awsVolume.getDevice() }); // データベースの更新 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setStatus(volume.getState()); awsVolume.setInstanceId(null); awsVolumeDao.update(awsVolume); } public void deleteVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); // イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = null; if (instanceNo != null) { instance = instanceDao.read(instanceNo); } processLogger.debug(component, instance, "AwsEbsDelete", new Object[] { awsProcessClient.getPlatform().getPlatformName(), awsVolume.getVolumeId() }); // ボリュームの削除 DeleteVolumeRequest request = new DeleteVolumeRequest(); request.withVolumeId(volumeId); awsProcessClient.getEc2Client().deleteVolume(request); // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100127", volumeId)); } } public void waitDeleteVolume(AwsProcessClient awsProcessClient, Long instanceNo, Long volumeNo) { AwsVolume awsVolume = awsVolumeDao.read(volumeNo); String volumeId = awsVolume.getVolumeId(); // ボリュームの削除待ち while (true) { try { Thread.sleep(1000L * awsProcessClient.getDescribeInterval()); } catch (InterruptedException ignore) { } try { awsCommonProcess.describeVolume(awsProcessClient, volumeId); } catch (AutoException ignore) { // 例外が発生したらボリュームが削除されて参照できなくなったものとみなす break; } } // イベントログ出力 Component component = null; if (awsVolume.getComponentNo() != null) { component = componentDao.read(awsVolume.getComponentNo()); } Instance instance = null; if (instanceNo != null) { instance = instanceDao.read(instanceNo); } processLogger.debug(component, instance, "AwsEbsDeleteFinish", new Object[] { awsProcessClient.getPlatform().getPlatformName(), awsVolume.getVolumeId() }); // ログ出力 if (log.isInfoEnabled()) { log.info(MessageUtils.getMessage("IPROCESS-100128", volumeId)); } // データベースの更新 awsVolume = awsVolumeDao.read(volumeNo); awsVolume.setVolumeId(null); awsVolume.setStatus(null); awsVolumeDao.update(awsVolume); } public void createTag(AwsProcessClient awsProcessClient, Long volumeNo) { // Eucalyptusの場合はタグを付けない PlatformAws platformAws = awsProcessClient.getPlatformAws(); if (BooleanUtils.isTrue(platformAws.getEuca())) { return; } AwsVolume awsVolume = awsVolumeDao.read(volumeNo); Component component = componentDao.read(awsVolume.getComponentNo()); Instance instance = instanceDao.read(awsVolume.getInstanceNo()); User user = userDao.read(awsProcessClient.getUserNo()); Farm farm = farmDao.read(instance.getFarmNo()); // タグを追加する List<Tag> tags = new ArrayList<Tag>(); if (component != null) { tags.add(new Tag("Name", instance.getFqdn() + "_" + component.getComponentName())); } else { String deviceName = awsVolume.getDevice().substring(awsVolume.getDevice().lastIndexOf("/") + 1); tags.add(new Tag("Name", instance.getFqdn() + "_" + deviceName)); } tags.add(new Tag("UserName", user.getUsername())); tags.add(new Tag("CloudName", farm.getDomainName())); tags.add(new Tag("ServerName", instance.getFqdn())); if (component != null) { tags.add(new Tag("ServiceName", component.getComponentName())); } awsCommonProcess.createTag(awsProcessClient, awsVolume.getVolumeId(), tags); } public void setAwsCommonProcess(AwsCommonProcess awsCommonProcess) { this.awsCommonProcess = awsCommonProcess; } public void setProcessLogger(ProcessLogger processLogger) { this.processLogger = processLogger; } }