/* * Copyright 2014 by SCSK Corporation. * * 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.component.windows.process; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import jp.primecloud.auto.common.log.LoggingUtils; import jp.primecloud.auto.common.status.ComponentInstanceStatus; import jp.primecloud.auto.entity.crud.AwsInstance; import jp.primecloud.auto.entity.crud.AwsVolume; import jp.primecloud.auto.entity.crud.CloudstackInstance; import jp.primecloud.auto.entity.crud.CloudstackVolume; import jp.primecloud.auto.entity.crud.Component; import jp.primecloud.auto.entity.crud.ComponentConfig; import jp.primecloud.auto.entity.crud.ComponentInstance; import jp.primecloud.auto.entity.crud.Instance; import jp.primecloud.auto.exception.MultiCauseException; import jp.primecloud.auto.util.MessageUtils; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import jp.primecloud.auto.component.windows.WindowsConstants; import jp.primecloud.auto.process.ComponentConstants; import jp.primecloud.auto.process.ComponentProcessContext; import jp.primecloud.auto.process.puppet.PuppetComponentProcess; /** * <p> * TODO: クラスコメントを記述 * </p> * */ public class WindowsComponentProcess extends PuppetComponentProcess { /** * TODO: コンストラクタコメントを記述 */ public WindowsComponentProcess() { componentTypeName = WindowsConstants.COMPONENT_TYPE_NAME; awsVolumeDevice = WindowsConstants.AWS_VOLUME_DEVICE; vmwareDiskScsiId = WindowsConstants.VMWARE_DISK_SCSI_ID; } @Override public void createNodeManifest(ComponentProcessContext context) { // ノード用マニフェストの生成 必要なし return; } @Override protected void configureInstances(final Long componentNo, final ComponentProcessContext context, final boolean start, List<Long> instanceNos) { Component component = componentDao.read(componentNo); List<Instance> instances = instanceDao.readInInstanceNos(instanceNos); Map<Long, Instance> instanceMap = new HashMap<Long, Instance>(); for (Instance instance : instances) { instanceMap.put(instance.getInstanceNo(), instance); } List<ComponentInstance> componentInstances = componentInstanceDao.readByComponentNo(componentNo); Map<Long, ComponentInstance> componentInstanceMap = new HashMap<Long, ComponentInstance>(); for (ComponentInstance componentInstance : componentInstances) { componentInstanceMap.put(componentInstance.getInstanceNo(), componentInstance); } // 停止を行う場合、既に停止しているサービスは除外する if (!start) { List<Long> tmpInstanceNos = new ArrayList<Long>(); for (Long instanceNo : instanceNos) { ComponentInstance componentInstance = componentInstanceMap.get(instanceNo); ComponentInstanceStatus status = ComponentInstanceStatus.fromStatus(componentInstance.getStatus()); if (status == ComponentInstanceStatus.STOPPED) { // 設定フラグを無効にする if (BooleanUtils.isTrue(componentInstance.getConfigure())) { componentInstance.setConfigure(false); componentInstanceDao.update(componentInstance); } } else { tmpInstanceNos.add(instanceNo); } } instanceNos = tmpInstanceNos; } if (instanceNos.isEmpty()) { return; } for (Long instanceNo : instanceNos) { ComponentInstance componentInstance = componentInstanceMap.get(instanceNo); ComponentInstanceStatus status = ComponentInstanceStatus.fromStatus(componentInstance.getStatus()); // ステータスを変更 if (start) { if (status == ComponentInstanceStatus.RUNNING) { status = ComponentInstanceStatus.CONFIGURING; } else { status = ComponentInstanceStatus.STARTING; } } else { status = ComponentInstanceStatus.STOPPING; } componentInstance.setStatus(status.toString()); componentInstanceDao.update(componentInstance); // イベントログ出力 if (BooleanUtils.isTrue(componentInstance.getConfigure())) { Instance instance = instanceMap.get(instanceNo); if (status == ComponentInstanceStatus.STARTING) { processLogger.info(component, instance, "ComponentStart", null); } else if (status == ComponentInstanceStatus.CONFIGURING) { processLogger.info(component, instance, "ComponentReload", null); } else if (status == ComponentInstanceStatus.STOPPING) { processLogger.info(component, instance, "ComponentStop", null); } } } // マニフェスト用情報モデルの作成 final Map<String, Object> rootMap = createComponentMap(componentNo, context, start); // 並列実行 List<Callable<Void>> callables = new ArrayList<Callable<Void>>(); final Map<String, Object> loggingContext = LoggingUtils.getContext(); for (final Long instanceNo : instanceNos) { Callable<Void> callable = new Callable<Void>() { @Override public Void call() throws Exception { LoggingUtils.setContext(loggingContext); try { doConfigureInstance(componentNo, context, start, instanceNo, rootMap); } catch (Exception e) { log.error(e.getMessage(), e); // イベントログ出力 eventLogger.error("SystemError", new Object[] { e.getMessage() }); throw e; } finally { LoggingUtils.removeContext(); } return null; } }; callables.add(callable); } try { List<Future<Void>> futures = executorService.invokeAll(callables); // 並列実行で例外発生時の処理 List<Throwable> throwables = new ArrayList<Throwable>(); for (Future<Void> future : futures) { try { future.get(); } catch (ExecutionException e) { throwables.add(e.getCause()); } catch (InterruptedException ignore) { } } // 例外を処理する if (throwables.size() > 0) { throw new MultiCauseException(throwables.toArray(new Throwable[throwables.size()])); } } catch (InterruptedException e) { } } private void doConfigureInstance(Long componentNo, ComponentProcessContext context, boolean start, Long instanceNo, Map<String, Object> rootMap) { Component component = componentDao.read(componentNo); Instance instance = instanceDao.read(instanceNo); // ログ用情報を格納 LoggingUtils.setInstanceNo(instanceNo); LoggingUtils.setInstanceName(instance.getInstanceName()); LoggingUtils.setInstanceType(processLogger.getInstanceType(instanceNo, instance.getPlatformNo())); LoggingUtils.setPlatformNo(instance.getPlatformNo()); if (log.isInfoEnabled()) { String code = start ? "IPROCESS-100221" : "IPROCESS-100223"; log.info(MessageUtils.getMessage(code, componentNo, instanceNo, component.getComponentName(), instance .getInstanceName())); } try { configureInstance(componentNo, context, start, instanceNo, rootMap); } catch (RuntimeException e) { if (start) { ComponentInstance componentInstance = componentInstanceDao.read(componentNo, instanceNo); ComponentInstanceStatus status = ComponentInstanceStatus.fromStatus(componentInstance.getStatus()); // イベントログ出力 if (BooleanUtils.isTrue(componentInstance.getConfigure())) { if (status == ComponentInstanceStatus.STARTING) { processLogger.info(component, instance, "ComponentStartFail", null); } else if (status == ComponentInstanceStatus.CONFIGURING) { processLogger.info(component, instance, "ComponentReloadFail", null); } else if (status == ComponentInstanceStatus.STOPPING) { processLogger.info(component, instance, "ComponentStopFail", null); } } // ステータス更新 if (status != ComponentInstanceStatus.WARNING || BooleanUtils.isTrue(componentInstance.getConfigure())) { componentInstance.setStatus(ComponentInstanceStatus.WARNING.toString()); componentInstance.setConfigure(false); componentInstanceDao.update(componentInstance); } throw e; } else { // 処理に失敗した場合、警告ログを出力する log.warn(e.getMessage()); } } ComponentInstance componentInstance = componentInstanceDao.read(componentNo, instanceNo); ComponentInstanceStatus status = ComponentInstanceStatus.fromStatus(componentInstance.getStatus()); // イベントログ出力 if (BooleanUtils.isTrue(componentInstance.getConfigure())) { if (status == ComponentInstanceStatus.STARTING) { processLogger.info(component, instance, "ComponentStartFinish", null); } else if (status == ComponentInstanceStatus.CONFIGURING) { processLogger.info(component, instance, "ComponentReloadFinish", null); } else if (status == ComponentInstanceStatus.STOPPING) { processLogger.info(component, instance, "ComponentStopFinish", null); } } // ステータスを変更 if (start) { if (status == ComponentInstanceStatus.CONFIGURING || status == ComponentInstanceStatus.STARTING || BooleanUtils.isTrue(componentInstance.getConfigure())) { componentInstance.setStatus(ComponentInstanceStatus.RUNNING.toString()); componentInstance.setConfigure(false); componentInstanceDao.update(componentInstance); } } else { if (status == ComponentInstanceStatus.STOPPING || BooleanUtils.isTrue(componentInstance.getConfigure())) { componentInstance.setStatus(ComponentInstanceStatus.STOPPED.toString()); componentInstance.setConfigure(false); componentInstanceDao.update(componentInstance); } } if (!start) { // コンポーネントとインスタンスの関連付けを削除 deleteAssociate(componentNo, instanceNo); } if (log.isInfoEnabled()) { String code = start ? "IPROCESS-100222" : "IPROCESS-100224"; log.info(MessageUtils.getMessage(code, componentNo, instanceNo, component.getComponentName(), instance .getInstanceName())); } } @Override protected void configureInstance(Long componentNo, ComponentProcessContext context, boolean start, Long instanceNo, Map<String, Object> rootMap) { if (start) { // ボリュームの開始処理 startVolume(componentNo, instanceNo); } else { // Zabbixテンプレートの終了処理 stopZabbixTemplate(componentNo, instanceNo); } if (!start) { // ボリュームの終了処理 stopVolume(componentNo, instanceNo); } else { // Zabbixテンプレートの開始処理 startZabbixTemplate(componentNo, instanceNo); } } @Override protected void generateManifest(String templateName, Map<String, Object> rootMap, File file, String encoding) { //必要なし return; } @Override protected void runPuppet(Instance instance, Component component) { //必要なし return; } @Override protected Map<String, Object> createComponentMap(Long componentNo, ComponentProcessContext context, boolean start) { Map<String, Object> map = new HashMap<String, Object>(); //必要なし return map; } @Override protected Map<String, Object> createInstanceMap(Long componentNo, ComponentProcessContext context, boolean start, Long instanceNo, Map<String, Object> rootMap) { Map<String, Object> map = new HashMap<String, Object>(rootMap); //必要なし return map; } @Override protected String getFileDigest(File file, String encoding) { //必要なし return null; } @Override protected void restoreManifest(Long componentNo, Long instanceNo) { //必要なし return; } @Override protected void backupManifest(Long componentNo, Long instanceNo) { //必要なし return; } @Override protected void deleteManifest(Long componentNo, Long instanceNo) { //必要なし return; } @Override protected void startAwsVolume(Long componentNo, Long instanceNo) { // デバイス名の取得 String device = getAwsVolumeDevice(); if (StringUtils.isEmpty(device)) { // デバイス名を取得できない場合はスキップ return; } // ボリューム情報の取得 AwsVolume awsVolume = awsVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); // ボリューム情報がない場合は作成する if (awsVolume == null) { // ディスクサイズの取得 Integer diskSize = null; ComponentConfig diskSizeConfig = componentConfigDao.readByComponentNoAndConfigName(componentNo, ComponentConstants.CONFIG_NAME_DISK_SIZE); if (diskSizeConfig != null) { try { diskSize = Integer.valueOf(diskSizeConfig.getConfigValue()); } catch (NumberFormatException ignore) { } } if (diskSize == null) { // ディスクサイズが指定されていない場合はスキップ return; } Instance instance = instanceDao.read(instanceNo); AwsInstance awsInstance = awsInstanceDao.read(instanceNo); awsVolume = new AwsVolume(); awsVolume.setFarmNo(instance.getFarmNo()); awsVolume.setVolumeName("vol"); awsVolume.setPlatformNo(instance.getPlatformNo()); awsVolume.setComponentNo(componentNo); awsVolume.setInstanceNo(instanceNo); awsVolume.setSize(diskSize); awsVolume.setAvailabilityZone(awsInstance.getAvailabilityZone()); awsVolume.setDevice(device); awsVolumeDao.create(awsVolume); } // ディスクがアタッチされておらず、アタッチするデバイスが異なる場合は変更する else if (StringUtils.isEmpty(awsVolume.getInstanceId()) && !StringUtils.equals(device, awsVolume.getDevice())) { awsVolume.setDevice(device); awsVolumeDao.update(awsVolume); } //GWは呼ばず作成済みへ持っていく // ボリューム情報の再取得 AwsVolume awsVolume2 = awsVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); awsVolume2.setStatus("in-use"); awsVolume2.setInstanceId("WindowsDummy"); awsVolumeDao.update(awsVolume2); } @Override protected void startCloudStackVolume(Long componentNo, Long instanceNo) { // ボリューム情報の取得 CloudstackVolume cloudstackVolume = cloudstackVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); // ボリューム情報がない場合は作成する if (cloudstackVolume == null) { // ディスクサイズの取得 Integer diskSize = null; ComponentConfig diskSizeConfig = componentConfigDao.readByComponentNoAndConfigName(componentNo, ComponentConstants.CONFIG_NAME_DISK_SIZE); if (diskSizeConfig != null) { try { diskSize = Integer.valueOf(diskSizeConfig.getConfigValue()); } catch (NumberFormatException ignore) { } } if (diskSize == null) { // ディスクサイズが指定されていない場合はスキップ return; } Instance instance = instanceDao.read(instanceNo); CloudstackInstance cloudstackInstance = cloudstackInstanceDao.read(instanceNo); //NAME ZONEID SIZE SNAPSHOTID DISKOFFERINGID が必要 // DISKOFFERINGID と SNAPSHOTID のいずれか一方が必要 cloudstackVolume = new CloudstackVolume(); cloudstackVolume.setFarmNo(instance.getFarmNo()); cloudstackVolume.setName("vol"); cloudstackVolume.setPlatformNo(instance.getPlatformNo()); cloudstackVolume.setComponentNo(componentNo); cloudstackVolume.setInstanceNo(instanceNo); cloudstackVolume.setSize(diskSize); cloudstackVolume.setZoneid(cloudstackInstance.getZoneid()); cloudstackVolumeDao.create(cloudstackVolume); }else if (cloudstackVolume.getInstanceId() != null && !"".equals(cloudstackVolume.getInstanceId())){ //すでにアタッチ済みの場合は処理を行わない //AWS等はGWでチェックし何もせずリターンするがPuppetの処理の都合上ここでリターンする return; } //GWは呼ばず作成済みへ持っていく // ボリューム情報の再取得 CloudstackVolume cloudstackVolume2 = cloudstackVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); cloudstackVolume2.setState("Allocated"); cloudstackVolume2.setInstanceId("WindowsDummy"); cloudstackVolumeDao.update(cloudstackVolume2); } @Override protected void stopAwsVolume(Long componentNo, Long instanceNo) { AwsVolume awsVolume = awsVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); if (awsVolume == null) { // ボリュームがない場合はスキップ return; } //GWは呼ばず開放へ持っていく awsVolume.setStatus("available"); awsVolume.setInstanceId(""); awsVolumeDao.update(awsVolume); } @Override protected void stopCloudStackVolume(Long componentNo, Long instanceNo) { CloudstackVolume cloudstackVolume = cloudstackVolumeDao.readByComponentNoAndInstanceNo(componentNo, instanceNo); if (cloudstackVolume == null) { // ボリュームがない場合はスキップ return; } //GWは呼ばず開放へ持っていく cloudstackVolume.setState("Releasing"); cloudstackVolume.setInstanceId(""); cloudstackVolumeDao.update(cloudstackVolume); } @Override protected void startZabbixTemplate(Long componentNo, Long instanceNo) { if (zabbixInstanceDao.countByInstanceNo(instanceNo) > 0) { zabbixHostProcess.startTemplate(instanceNo, componentNo); } } @Override protected void stopZabbixTemplate(Long componentNo, Long instanceNo) { if (zabbixInstanceDao.countByInstanceNo(instanceNo) > 0) { try { zabbixHostProcess.stopTemplate(instanceNo, componentNo); } catch (Exception e) { log.warn(e.getMessage()); } } } }