/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.geo.vdccontroller.impl;
import java.net.URI;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.emc.storageos.geomodel.VdcCertListParam;
import com.emc.storageos.geomodel.VdcPreCheckParam2;
import com.emc.storageos.geomodel.VdcPreCheckResponse2;
import com.emc.storageos.geomodel.VdcConfig;
import com.emc.storageos.security.ipsec.IPsecConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.coordinator.common.Service;
import com.emc.storageos.db.client.model.VirtualDataCenter;
import com.emc.storageos.db.client.model.VirtualDataCenter.ConnectionStatus;
import com.emc.storageos.security.geo.exceptions.GeoException;
import com.emc.storageos.geo.service.impl.util.VdcConfigHelper;
import com.emc.storageos.security.geo.GeoClientCacheManager;
/*
* Detail implementation of vdc reconnect operation
*/
public class ReconnectVdcTaskOp extends AbstractVdcTaskOp {
private final static Logger log = LoggerFactory.getLogger(ReconnectVdcTaskOp.class);
private final static int NODE_CHECK_TIMEOUT = 60 * 1000; // one minute
public ReconnectVdcTaskOp(InternalDbClient dbClient, GeoClientCacheManager geoClientCache,
VdcConfigHelper helper, Service serviceInfo, VirtualDataCenter vdc, String taskId, KeyStore keystore, IPsecConfig ipsecConfig) {
super(dbClient, geoClientCache, helper, serviceInfo, vdc, taskId, null, keystore, ipsecConfig);
}
@Override
protected void process() {
log.info("Start reconnect vdc operation to vdc {}", operatedVdc.getId());
loadVdcInfo();
log.info("Load vdc info is done");
preCheck();
reconnectVdc();
// release global lock, same way with connect vdc.
postStep();
log.info("Reconnect vdc done");
}
private void preCheck() {
URI reconnectVdcId = operatedVdc.getId();
URI unstable = checkAllVdcStable(true, true);
if (unstable != null) {
log.error("The 'reconnect vdc operation' should not be triggered because vdc {} is unstable", unstable);
VirtualDataCenter vdc = dbClient.queryObject(VirtualDataCenter.class, unstable);
String vdcName = (vdc != null) ? vdc.getLabel() : "";
throw GeoException.fatals.unstableVdcFailure(vdcName);
}
if (!isReconnectVdcInRightStatus()) {
log.error("The vdc {} to be reconnected has wrong status {}", reconnectVdcId, operatedVdcStatus);
throw GeoException.fatals.reconnectVdcInvalidStatus(operatedVdc.getLabel());
}
// Only reconnect the vdc back once it is connected with other connected vdcs.
if (!isAllConnectedVdcReachableWith(operatedVdc)) {
log.error("There is at least one vdc is unreachable with the vdc {} which will need to be reconnected ", operatedVdc);
throw GeoException.fatals.reconnectVdcUnreachable(errMsg);
}
// Only reconnect the vdc back if myVdc is connected with other connected vdcs.
if (!isAllConnectedVdcReachableWith(myVdc)) {
log.error("There is at least one vdc is unreachable with the vdc {} which will perform this operation ", myVdcId);
throw GeoException.fatals.reconnectVdcUnreachable(errMsg);
}
if (isVdcVersion20()) {
log.error("At least one vdc's version is less than 2.1");
throw GeoException.fatals.vdcVersionCheckFail(errMsg);
}
// TODO: check if the vdc need to be reconnected back's IP and SSL changed or not. Or any related change.
checkReconnectingVdc();
log.info("ReconnectVdcTaskOp precheck phrase success");
}
private void checkReconnectingVdc() {
VdcPreCheckParam2 param = new VdcPreCheckParam2();
param.setConfigChangeType(changeType());
Map<String, List<String>> blackLists = dbClient.getBlacklist();
List<String> blackList = new ArrayList();
Collection<List<String>> lists = blackLists.values();
for (List<String> list : lists) {
blackList = list;
// since all lists are same, so we only need the first one
break;
}
param.setBlackList(blackList);
List<String> whiteList = getWhiteList();
param.setWhiteList(whiteList);
List<URI> ids = new ArrayList(1);
ids.add(myVdc.getId());
param.setVdcIds(ids);
log.info("checkReconnectingVdc param={}", param);
VdcPreCheckResponse2 resp2 = null;
try {
resp2 = sendVdcPrecheckRequest2(operatedVdc, param, NODE_CHECK_TIMEOUT);
} catch (Exception ex) {
log.error("Precheck the reconnected vdc {} failed: {}", operatedVdc.getShortId(), ex);
throw ex;
}
if (resp2.getCompatible() == false) {
log.error("Precheck the reconnected vdc {} failed", operatedVdc.getShortId());
throw GeoException.fatals.reconnectVdcIncompatible();
}
log.info("The precheck reconnect vdc {} is passed", operatedVdc.getShortId());
}
private List<String> getWhiteList() {
List<String> whiteList = new ArrayList();
List<URI> ids = dbClient.queryByType(VirtualDataCenter.class, true);
for (URI id : ids) {
VirtualDataCenter vdc = dbClient.queryObject(VirtualDataCenter.class, id);
if (vdc.getConnectionStatus() == ConnectionStatus.CONNECTED) {
Collection<String> addresses = dbClient.queryHostIPAddressesMap(vdc).values();
whiteList.addAll(addresses);
}
}
return whiteList;
}
private void reconnectVdc() {
// TODO: use updateVdcStatus() later
updateOpStatus(ConnectionStatus.RECONNECTING);
failedVdcStatus = ConnectionStatus.RECONNECT_FAILED;
lockHelper.acquire(operatedVdc.getShortId());
log.info("Acquired global lock, go on with reconnect vdc");
removeVdcFromBlacklist();
setStrategyOption();
// Sync cert for operated vdc, incase there is any add or delete vdc after it has been disconnected.
syncCertForOperatedVdc();
// Update status for other living vdcs
updateVdcStatus(ConnectionStatus.CONNECTED, false);
// update config for operated vdc, will sync config and trigger node repair
updateVdcStatus(ConnectionStatus.CONNECTED, true);
}
private void postStep() {
lockHelper.release(operatedVdc.getShortId());
}
/*
* sync cert for operated vdc incase there is any add or delete vdc after it has been disconnected.
* Will simulate the add vdc operation, add all existing certs from myVdc to operatedVdc
*/
private void syncCertForOperatedVdc() {
VdcCertListParam certListParam = genCertListParam(VdcCertListParam.CMD_ADD_CERT);
syncCertForSingleVdc(certListParam, operatedVdc);
}
private void setStrategyOption() {
try {
helper.addStrategyOption(operatedVdc, true);
} catch (Exception e) {
log.error("e= ", e);
throw GeoException.fatals.vdcStrategyFailed(e);
}
}
private void removeVdcFromBlacklist() {
try {
dbClient.removeVdcNodesFromBlacklist(operatedVdc);
} catch (Exception e) {
throw GeoException.fatals.failedRemoveNodesFromBlackList(myVdc.getLabel(), operatedVdc.getId().toString(), e);
}
}
/*
* This function is only used for reconnect vdc pre check. Do not allow below three status to reconnect,
* because in such situation, the vdc might not in the fedration.
*/
private boolean isReconnectVdcInRightStatus() {
if (operatedVdcStatus == VirtualDataCenter.ConnectionStatus.CONNECT_FAILED ||
operatedVdcStatus == VirtualDataCenter.ConnectionStatus.REMOVE_FAILED) {
return false;
} else {
return true;
}
}
@Override
public VdcConfig.ConfigChangeType changeType() {
return VdcConfig.ConfigChangeType.RECONNECT_VDC;
}
}