/*
* 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.nifty;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import jp.primecloud.auto.config.Config;
import jp.primecloud.auto.entity.crud.Farm;
import jp.primecloud.auto.entity.crud.Image;
import jp.primecloud.auto.entity.crud.ImageNifty;
import jp.primecloud.auto.entity.crud.Instance;
import jp.primecloud.auto.entity.crud.NiftyInstance;
import jp.primecloud.auto.entity.crud.NiftyKeyPair;
import jp.primecloud.auto.entity.crud.Platform;
import jp.primecloud.auto.exception.AutoException;
import jp.primecloud.auto.nifty.dto.InstanceDto;
import jp.primecloud.auto.nifty.process.NiftyProcessClient;
import jp.primecloud.auto.process.ProcessLogger;
import jp.primecloud.auto.service.ServiceSupport;
import jp.primecloud.auto.util.JSchUtils;
import jp.primecloud.auto.util.JSchUtils.JSchResult;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.math.NumberUtils;
import com.jcraft.jsch.Session;
/**
* <p>
* TODO: クラスコメントを記述
* </p>
*
*/
public class NiftyInstanceProcess extends ServiceSupport {
protected ProcessLogger processLogger;
private final static Object lock = new Object();
public void createInstance(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
// インスタンスIDがある場合はスキップ
if (StringUtils.isNotEmpty(niftyInstance.getInstanceId())) {
return;
}
// インスタンスの作成と起動
run(niftyProcessClient, instanceNo);
// 初期化スクリプトを実行
init(niftyProcessClient, instanceNo);
// インスタンスの停止
stop(niftyProcessClient, instanceNo);
}
public void startInstance(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
// インスタンスIDがない場合はエラー
if (StringUtils.isEmpty(niftyInstance.getInstanceId())) {
// TODO: エラーコード
throw new RuntimeException();
}
// インスタンスの起動
start(niftyProcessClient, instanceNo);
}
public void stopInstance(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
// インスタンスIDがない場合はスキップ
if (StringUtils.isEmpty(niftyInstance.getInstanceId())) {
return;
}
// インスタンスの停止
stop(niftyProcessClient, instanceNo);
// 初期化フラグが立っていない場合は削除する
niftyInstance = niftyInstanceDao.read(instanceNo);
if (BooleanUtils.isNotTrue(niftyInstance.getInitialized())) {
terminate(niftyProcessClient, instanceNo);
}
}
public void deleteInstance(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
// インスタンスIDがない場合はスキップ
if (StringUtils.isEmpty(niftyInstance.getInstanceId())) {
return;
}
// インスタンスの削除
terminate(niftyProcessClient, instanceNo);
}
protected void run(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
// イメージを取得
Instance instance = instanceDao.read(instanceNo);
ImageNifty imageNifty = imageNiftyDao.read(instance.getImageNo());
// キーペアを取得
NiftyKeyPair niftyKeyPair = niftyKeyPairDao.read(niftyInstance.getKeyPairNo());
// 排他制御(apiを同時に実行するとエラーになる対策)
synchronized(lock) {
// イベントログ出力
Platform platform = platformDao.read(niftyProcessClient.getPlatformNo());
processLogger.debug(null, instance, "NiftyInstanceCreate",new Object[] { platform.getPlatformName() });
// インスタンスの起動
// TODO: rootパスワードを決める
InstanceDto instance2 = niftyProcessClient.runInstance(imageNifty.getImageId(), niftyKeyPair
.getKeyName(), "mini", "password");
String instanceId = instance2.getInstanceId();
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setInstanceId(instanceId);
niftyInstanceDao.update(niftyInstance);
// インスタンスの起動待ち
instance2 = niftyProcessClient.waitRunInstance(instanceId);
// イベントログ出力
processLogger.debug(null, instance, "NiftyInstanceCreateFinish", new Object[] { platform.getPlatformName(),
instance2.getInstanceId() });
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setStatus(instance2.getState().getName());
niftyInstance.setDnsName(instance2.getDnsName());
niftyInstance.setPrivateDnsName(instance2.getPrivateDnsName());
niftyInstance.setIpAddress(instance2.getIpAddress());
niftyInstance.setPrivateIpAddress(instance2.getPrivateIpAddress());
niftyInstanceDao.update(niftyInstance);
}
}
protected void start(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
String instanceId = niftyInstance.getInstanceId();
// 排他制御(apiを同時に実行するとエラーになる対策)
synchronized(lock) {
// イベントログ出力
Instance instance = instanceDao.read(instanceNo);
Platform platform = platformDao.read(niftyProcessClient.getPlatformNo());
processLogger.debug(null, instance, "NiftyInstanceStart", new Object[] { platform.getPlatformName(),
instanceId });
// インスタンスの開始
niftyProcessClient.startInstance(instanceId, niftyInstance.getInstanceType());
// インスタンスの開始待ち
InstanceDto instance2 = niftyProcessClient.waitStartInstance(instanceId);
// イベントログ出力
processLogger.debug(null, instance, "NiftyInstanceStartFinish", new Object[] { platform.getPlatformName(),
instanceId });
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setStatus(instance2.getState().getName());
niftyInstance.setDnsName(instance2.getDnsName());
niftyInstance.setPrivateDnsName(instance2.getPrivateDnsName());
niftyInstance.setIpAddress(instance2.getIpAddress());
niftyInstance.setPrivateIpAddress(instance2.getPrivateIpAddress());
niftyInstanceDao.update(niftyInstance);
}
}
protected void stop(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
String instanceId = niftyInstance.getInstanceId();
// 排他制御(apiを同時に実行するとエラーになる対策)
synchronized(lock) {
// イベントログ出力
Instance instance = instanceDao.read(instanceNo);
Platform platform = platformDao.read(niftyProcessClient.getPlatformNo());
processLogger.debug(null, instance, "NiftyInstanceStop", new Object[] { platform.getPlatformName(),
instanceId });
// インスタンスの停止
niftyProcessClient.stopInstance(instanceId);
// インスタンスの停止待ち
InstanceDto instance2 = niftyProcessClient.waitStopInstance(instanceId);
// イベントログ出力
processLogger.debug(null, instance, "NiftyInstanceStopFinish", new Object[] { platform.getPlatformName(),
instanceId });
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setStatus(instance2.getState().getName());
niftyInstance.setDnsName(instance2.getDnsName());
niftyInstance.setPrivateDnsName(instance2.getPrivateDnsName());
niftyInstance.setIpAddress(instance2.getIpAddress());
niftyInstance.setPrivateIpAddress(instance2.getPrivateIpAddress());
niftyInstanceDao.update(niftyInstance);
}
}
protected void terminate(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
String instanceId = niftyInstance.getInstanceId();
synchronized(lock) {
try {
// イベントログ出力
Instance instance = instanceDao.read(instanceNo);
Platform platform = platformDao.read(niftyProcessClient.getPlatformNo());
processLogger.debug(null, instance, "NiftyInstanceDelete", new Object[] { platform.getPlatformName(),
instanceId });
// インスタンスの削除
niftyProcessClient.terminateInstance(instanceId);
// インスタンスの削除待ち
niftyProcessClient.waitTerminateInstance(instanceId);
// イベントログ出力
processLogger.debug(null, instance, "NiftyInstanceDeleteFinish", new Object[] { platform.getPlatformName(),
instanceId });
} catch (AutoException ignore) {
// インスタンス削除異常終了時は、警告ログを出力して例外を握りつぶす
log.warn(ignore.getMessage());
}
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setInstanceId(null);
niftyInstance.setStatus(null);
niftyInstance.setDnsName(null);
niftyInstance.setPrivateDnsName(null);
niftyInstance.setIpAddress(null);
niftyInstance.setPrivateIpAddress(null);
niftyInstance.setInitialized(null);
niftyInstanceDao.update(niftyInstance);
}
}
protected void init(NiftyProcessClient niftyProcessClient, Long instanceNo) {
NiftyInstance niftyInstance = niftyInstanceDao.read(instanceNo);
NiftyKeyPair niftyKeyPair = niftyKeyPairDao.read(niftyInstance.getKeyPairNo());
// イベントログ出力
String instanceId = niftyInstance.getInstanceId();
Instance instance = instanceDao.read(instanceNo);
Platform platform = platformDao.read(niftyProcessClient.getPlatformNo());
processLogger.debug(null, instance, "NiftyInstanceInitialize", new Object[] { platform.getPlatformName(),
instanceId });
// SSHセッションを作成
Session session = JSchUtils.createSessionByPrivateKey("root", niftyKeyPair.getPrivateKey(), niftyKeyPair
.getPassphrase(), niftyInstance.getIpAddress());
try {
// 初期化スクリプトを転送
Image image = imageDao.read(instance.getImageNo());
String imageDir = Config.getProperty("nifty.imageDir");
File imageFile = new File(imageDir, image.getImageName() + ".tar");
FileInputStream input = null;
try {
input = new FileInputStream(imageFile);
JSchUtils.sftpPut(session, input, "/tmp/image.tar");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(input);
}
// ユーザデータを作成
String userData = createUserData(instanceNo);
// 初期化スクリプトを実行
String command = "mkdir -p /tmp/image/ && tar xvf /tmp/image.tar -C /tmp/image/ && /tmp/image/init.sh \""
+ userData + "\"";
String initTimeout = Config.getProperty("nifty.initTimeout");
long timeout = NumberUtils.toLong(initTimeout, 60 * 30 * 1000L);
JSchResult result = JSchUtils.executeCommand(session, command, "UTF-8", timeout, true);
if (result.getExitStatus() != 0) {
// 初期化スクリプトの実行に失敗
AutoException exception = new AutoException("EPROCESS-000616", instanceNo);
exception.addDetailInfo("result="
+ ReflectionToStringBuilder.toString(result, ToStringStyle.SHORT_PREFIX_STYLE));
throw exception;
}
} finally {
if (session != null) {
session.disconnect();
}
}
// イベントログ出力
processLogger.debug(null, instance, "NiftyInstanceInitializeFinish", new Object[] { platform.getPlatformName(),
instanceId });
// データベース更新
niftyInstance = niftyInstanceDao.read(instanceNo);
niftyInstance.setInitialized(true);
niftyInstanceDao.update(niftyInstance);
}
protected String createUserData(Long instanceNo) {
Map<String, String> map = createUserDataMap(instanceNo);
if (map.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key != null && value != null) {
sb.append(key).append("=").append(value).append(";");
}
}
sb.delete(sb.length() - 1, sb.length());
return sb.toString();
}
protected Map<String, String> createUserDataMap(Long instanceNo) {
Instance instance = instanceDao.read(instanceNo);
Farm farm = farmDao.read(instance.getFarmNo());
Map<String, String> map = new HashMap<String, String>();
// DB情報
map.put("instanceName", instance.getInstanceName());
map.put("farmName", farm.getFarmName());
// FQDN
String fqdn = instance.getFqdn();
map.put("hostname", fqdn);
// 初期スクリプト情報
map.put("scriptserver", Config.getProperty("script.server"));
// DNS情報
map.putAll(createDnsUserDataMap(instanceNo));
// Puppet情報
map.putAll(createPuppetUserDataMap(instanceNo));
// VPN情報
Platform platform = platformDao.read(instance.getPlatformNo());
if (platform.getInternal() == false) {
// 外部のプラットフォームの場合、VPN情報を含める
map.putAll(createVpnUserDataMap(instanceNo));
}
// OpenVPNクライアント証明書ダウンロード先URL
map.put("vpnclienturl", Config.getProperty("vpn.clienturl"));
return map;
}
protected Map<String, String> createDnsUserDataMap(Long instanceNo) {
Map<String, String> map = new HashMap<String, String>();
// Primary DNSサーバ
map.put("dns", Config.getProperty("dns.server"));
// Secondry DNSサーバ
String dns2 = Config.getProperty("dns.server2");
if (dns2 != null && dns2.length() > 0) {
map.put("dns2", dns2);
}
// DNSドメイン
map.put("dnsdomain", Config.getProperty("dns.domain"));
return map;
}
protected Map<String, String> createPuppetUserDataMap(Long instanceNo) {
Map<String, String> map = new HashMap<String, String>();
// PuppetMaster情報
map.put("puppetmaster", Config.getProperty("puppet.masterHost"));
return map;
}
protected Map<String, String> createVpnUserDataMap(Long instanceNo) {
Map<String, String> map = new HashMap<String, String>();
// VPN情報のユーザとパスワードをセットする
Instance instance = instanceDao.read(instanceNo);
map.put("vpnuser", instance.getFqdn());
map.put("vpnuserpass", instance.getInstanceCode());
// VPNサーバ情報
map.put("vpnserver", Config.getProperty("vpn.server"));
map.put("vpnport", Config.getProperty("vpn.port"));
//map.put("vpnuser", Config.getProperty("vpn.user"));
//map.put("vpnuserpass", Config.getProperty("vpn.userpass"));
// ZIPパスワード
map.put("vpnzippass", Config.getProperty("vpn.zippass"));
return map;
}
/**
* processLoggerを設定します。
*
* @param processLogger processLogger
*/
public void setProcessLogger(ProcessLogger processLogger) {
this.processLogger = processLogger;
}
}