/*
* 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.process;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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.ExecutorService;
import java.util.concurrent.Future;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import jp.primecloud.auto.common.constant.PCCConstant;
import jp.primecloud.auto.common.log.LoggingUtils;
import jp.primecloud.auto.common.status.InstanceStatus;
import jp.primecloud.auto.common.status.LoadBalancerStatus;
import jp.primecloud.auto.entity.crud.Component;
import jp.primecloud.auto.entity.crud.ComponentInstance;
import jp.primecloud.auto.entity.crud.ComponentType;
import jp.primecloud.auto.entity.crud.Farm;
import jp.primecloud.auto.entity.crud.Image;
import jp.primecloud.auto.entity.crud.Instance;
import jp.primecloud.auto.entity.crud.LoadBalancer;
import jp.primecloud.auto.entity.crud.PuppetInstance;
import jp.primecloud.auto.exception.MultiCauseException;
import jp.primecloud.auto.process.lb.LoadBalancerProcess;
import jp.primecloud.auto.process.puppet.PuppetComponentProcess;
import jp.primecloud.auto.service.ServiceSupport;
import jp.primecloud.auto.util.MessageUtils;
/**
* <p>
* TODO: クラスコメントを記述
* </p>
*
*/
public class ComponentProcess extends ServiceSupport {
protected PuppetComponentProcess puppetComponentProcess;
protected PuppetComponentProcess[] extPuppetComponentProcesses;
protected LoadBalancerProcess loadBalancerProcess;
protected ExecutorService executorService;
public void configure(Long farmNo) {
Farm farm = farmDao.read(farmNo);
if (BooleanUtils.isTrue(farm.getComponentProcessing())) {
// 処理中のため実行できない場合
if (log.isDebugEnabled()) {
String message = MessageUtils.format("Component is being configured.(farmNo={0})", farmNo);
log.debug(message);
}
return;
}
if (log.isInfoEnabled()) {
log.info(MessageUtils.getMessage("IPROCESS-100011", farmNo));
}
// ステータス変更
farm.setComponentProcessing(true);
farmDao.update(farm);
try {
// 処理対象の情報を取得
ComponentProcessContext context = createContext(farmNo);
List<Component> components = null;
if (!context.getRunningInstanceNos().isEmpty()) {
// インスタンスのノードのマニフェストを生成
puppetComponentProcess.createNodeManifest(context);
components = getComponents(farmNo);
}
// コンポーネントに関する開始処理
if (!context.getRunningInstanceNos().isEmpty()) {
for (int i = 0; i < components.size(); i++) {
Component component = components.get(i);
// ログ用情報を格納
LoggingUtils.setComponentNo(component.getComponentNo());
LoggingUtils.setComponentName(component.getComponentName());
startPuppet(components.get(i), context);
// 正常に終了した場合のみログ用情報をクリアする
LoggingUtils.setComponentNo(null);
LoggingUtils.setComponentName(null);
}
}
// ロードバランサの設定処理
if (!context.getTargetLoadBalancerNos().isEmpty()) {
// 並列実行
List<Callable<Void>> callables = new ArrayList<Callable<Void>>();
final Map<String, Object> loggingContext = LoggingUtils.getContext();
for (final Long loadBalancerNo : context.getTargetLoadBalancerNos()) {
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
LoggingUtils.setContext(loggingContext);
try {
loadBalancerProcess.configure(loadBalancerNo);
} catch (Exception e) {
log.error(e.getMessage(), e);
// TODO: イベントログ出力
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) {
}
}
// コンポーネントに関する終了処理
if (!context.getRunningInstanceNos().isEmpty()) {
for (int i = components.size() - 1; i >= 0; i--) {
Component component = components.get(i);
// ログ用情報を格納
LoggingUtils.setComponentNo(component.getComponentNo());
LoggingUtils.setComponentName(component.getComponentName());
stopPuppet(components.get(i), context);
// 正常に終了した場合のみログ用情報をクリアする
LoggingUtils.setComponentNo(null);
LoggingUtils.setComponentName(null);
}
}
} finally {
// ステータス変更
farm = farmDao.read(farmNo);
farm.setComponentProcessing(false);
farmDao.update(farm);
}
if (log.isInfoEnabled()) {
log.info(MessageUtils.getMessage("IPROCESS-100012", farmNo));
}
}
protected ComponentProcessContext createContext(Long farmNo) {
ComponentProcessContext context = new ComponentProcessContext();
context.setFarmNo(farmNo);
// 処理対象のサーバを取得
List<Instance> instances = instanceDao.readByFarmNo(farmNo);
List<Long> runningInstanceNos = new ArrayList<Long>();
for (Instance instance : instances) {
// サーバが起動していない場合は対象外
if (InstanceStatus.fromStatus(instance.getStatus()) != InstanceStatus.RUNNING) {
continue;
}
// ロードバランサのサーバは対象外
if (BooleanUtils.isTrue(instance.getLoadBalancer())) {
continue;
}
// PuppetInstanceレコードがなければ対象外 ただしOSがwindowsの場合はカウントする
PuppetInstance puppetInstance = puppetInstanceDao.read(instance.getInstanceNo());
Image image = imageDao.read(instance.getImageNo());
if (puppetInstance == null && !StringUtils.startsWithIgnoreCase(image.getOs(), PCCConstant.OS_NAME_WIN)) {
continue;
}
runningInstanceNos.add(instance.getInstanceNo());
}
context.setRunningInstanceNos(runningInstanceNos);
// 処理対象のコンポーネントを取得
List<ComponentInstance> componentInstances = componentInstanceDao.readInInstanceNos(runningInstanceNos);
Map<Long, List<Long>> enableInstanceNoMap = new HashMap<Long, List<Long>>();
Map<Long, List<Long>> disableInstanceNoMap = new HashMap<Long, List<Long>>();
for (ComponentInstance componentInstance : componentInstances) {
Map<Long, List<Long>> map;
if (BooleanUtils.isTrue(componentInstance.getEnabled())) {
map = enableInstanceNoMap;
} else {
map = disableInstanceNoMap;
}
List<Long> list = map.get(componentInstance.getComponentNo());
if (list == null) {
list = new ArrayList<Long>();
map.put(componentInstance.getComponentNo(), list);
}
list.add(componentInstance.getInstanceNo());
}
context.setEnableInstanceNoMap(enableInstanceNoMap);
context.setDisableInstanceNoMap(disableInstanceNoMap);
// 処理対象のロードバランサを取得
List<LoadBalancer> loadBalancers = loadBalancerDao.readByFarmNo(farmNo);
List<Long> loadBalancerNos = new ArrayList<Long>();
for (LoadBalancer loadBalancer : loadBalancers) {
LoadBalancerStatus status = LoadBalancerStatus.fromStatus(loadBalancer.getStatus());
if (BooleanUtils.isTrue(loadBalancer.getEnabled())) {
// 有効なロードバランサの場合、起動状態でなければ対象外
if (status != LoadBalancerStatus.RUNNING) {
continue;
}
} else {
// 無効なロードバランサの場合、起動または異常状態でなければ対象外
if (status != LoadBalancerStatus.RUNNING && status != LoadBalancerStatus.WARNING) {
continue;
}
}
loadBalancerNos.add(loadBalancer.getLoadBalancerNo());
}
context.setTargetLoadBalancerNos(loadBalancerNos);
return context;
}
protected List<Component> getComponents(Long farmNo) {
// コンポーネントのリスト取得
List<Component> components = componentDao.readByFarmNo(farmNo);
// ロードバランサコンポーネントを除外
List<Component> tmpComponents = new ArrayList<Component>();
for (Component component : components) {
if (BooleanUtils.isTrue(component.getLoadBalancer())) {
continue;
}
tmpComponents.add(component);
}
components = tmpComponents;
// コンポーネントを昇順でソート
Comparator<Component> comparator = new Comparator<Component>() {
@Override
public int compare(Component o1, Component o2) {
ComponentType c1 = componentTypeDao.read(o1.getComponentTypeNo());
ComponentType c2 = componentTypeDao.read(o2.getComponentTypeNo());
Integer runOrder1 = c1.getRunOrder();
Integer runOrder2 = c2.getRunOrder();
int ro1 = runOrder1 == null ? 0 : runOrder1.intValue();
int ro2 = runOrder2 == null ? 0 : runOrder2.intValue();
return ro1 - ro2;
}
};
Collections.sort(components, comparator);
return components;
}
protected void startPuppet(Component component, ComponentProcessContext context) {
// ComponentTypeNameが一致する拡張クラスがあれば、それを利用する
PuppetComponentProcess puppetComponentProcess = this.puppetComponentProcess;
if (extPuppetComponentProcesses != null) {
ComponentType componentType = componentTypeDao.read(component.getComponentTypeNo());
for (PuppetComponentProcess process : extPuppetComponentProcesses) {
if (StringUtils.equals(componentType.getComponentTypeName(), process.getComponentTypeName())) {
puppetComponentProcess = process;
break;
}
}
}
puppetComponentProcess.startComponent(component.getComponentNo(), context);
}
protected void stopPuppet(Component component, ComponentProcessContext context) {
// ComponentTypeNameが一致する拡張クラスがあれば、それを利用する
PuppetComponentProcess puppetComponentProcess = this.puppetComponentProcess;
if (extPuppetComponentProcesses != null) {
ComponentType componentType = componentTypeDao.read(component.getComponentTypeNo());
for (PuppetComponentProcess process : extPuppetComponentProcesses) {
if (StringUtils.equals(componentType.getComponentTypeName(), process.getComponentTypeName())) {
puppetComponentProcess = process;
break;
}
}
}
try {
puppetComponentProcess.stopComponent(component.getComponentNo(), context);
} catch (RuntimeException e) {
log.warn(e.getMessage(), e);
}
}
/**
* puppetComponentProcessを設定します。
*
* @param puppetComponentProcess puppetComponentProcess
*/
public void setPuppetComponentProcess(PuppetComponentProcess puppetComponentProcess) {
this.puppetComponentProcess = puppetComponentProcess;
}
/**
* extPuppetComponentProcessesを設定します。
*
* @param extPuppetComponentProcesses extPuppetComponentProcesses
*/
public void setExtPuppetComponentProcesses(PuppetComponentProcess[] extPuppetComponentProcesses) {
this.extPuppetComponentProcesses = extPuppetComponentProcesses;
}
/**
* loadBalancerProcessを設定します。
*
* @param loadBalancerProcess loadBalancerProcess
*/
public void setLoadBalancerProcess(LoadBalancerProcess loadBalancerProcess) {
this.loadBalancerProcess = loadBalancerProcess;
}
/**
* executorServiceを設定します。
*
* @param executorService executorService
*/
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
}