/*
* 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.List;
import com.emc.storageos.geomodel.VdcPreCheckParam2;
import com.emc.storageos.geomodel.VdcPreCheckResponse2;
import com.emc.storageos.security.ipsec.IPsecConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.geo.service.impl.util.VdcConfigHelper;
import com.emc.storageos.geomodel.VdcConfig;
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.security.geo.GeoClientCacheManager;
/*
* Detail implementation of vdc disconnect operation
*/
public class DisconnectVdcTaskOp extends AbstractVdcTaskOp {
private final static Logger log = LoggerFactory.getLogger(DisconnectVdcTaskOp.class);
private final static int NODE_CHECK_TIMEOUT = 180 * 1000; // 3 minutes
public DisconnectVdcTaskOp(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 disconnect vdc");
loadVdcInfo();
log.info("Load vdc info is done");
preCheck();
disconnectVdc();
log.info("Disconnect vdc done");
}
private void preCheck() {
log.info("The precheck phrase of 'disconnect vdc' start ...");
checkDisconnectingConcurrency();
// TODO: use updateVdcStatus()
updateOpStatus(ConnectionStatus.DISCONNECTING);
URI unstable = checkAllVdcStable(true, true);
if (unstable != null) {
notifyPrecheckFailed();
log.error("The 'disconnect vdc operation' should not be triggered because vdc {} is unstable", unstable.toString());
VirtualDataCenter vdc = dbClient.queryObject(VirtualDataCenter.class, unstable);
String vdcName = (vdc != null) ? vdc.getLabel() : "";
throw GeoException.fatals.unstableVdcFailure(vdcName);
}
if (isTargetVdcReachable(NODE_CHECK_TIMEOUT)) {
notifyPrecheckFailed();
log.error("The vdc {} to be disconnected is still reachable from other VDCs", operatedVdc.getId());
throw GeoException.fatals.disconnectVdcStillReachable(operatedVdc.getLabel());
}
if (isVdcVersion20()) {
notifyPrecheckFailed();
log.error("At least one vdc's version is less than 2.1");
throw GeoException.fatals.vdcVersionCheckFail(errMsg);
}
log.info("The precheck phrase of 'disconnect vdc' successes");
}
private void checkDisconnectingConcurrency() {
// Reject the disconnect request if there is any vdc that is under disconnecting
// check local db first
VirtualDataCenter disconnectingVdc = helper.getDisconnectingVdc();
if (disconnectingVdc != null)
{
log.error("There is already a VDC {} under disconnecting", disconnectingVdc.getId());
throw GeoException.fatals.disconnectVdcConcurrentCheckFail(disconnectingVdc.getLabel());
}
for (VirtualDataCenter vdc : connectedVdc) {
if (operatedVdc.getId().equals(vdc.getId()) || myVdcId.equals(vdc.getId().toString())) {
continue; // Don't check on the vdc to be disconnected and myself
}
VdcPreCheckParam2 param = new VdcPreCheckParam2();
param.setConfigChangeType(changeType());
List<URI> vdcIds = new ArrayList(2);
vdcIds.add(operatedVdc.getId());
vdcIds.add(myVdc.getId());
param.setVdcIds(vdcIds);
log.info("'disconnect vdc' precheck2 paramerte={}", param);
VdcPreCheckResponse2 resp2 = null;
try {
resp2 = sendVdcPrecheckRequest2(vdc, param, DEFAULT_NODE_CHECK_TIMEOUT);
} catch (Exception ex) {
log.error("Precheck the reconnected vdc {} failed: {}", operatedVdc.getShortId(), ex);
notifyPrecheckFailed();
throw ex;
}
if (resp2.getId() != null) {
log.error("There is already a VDC {} under disconnecting", disconnectingVdc);
notifyPrecheckFailed();
throw GeoException.fatals.disconnectVdcConcurrentCheckFail(disconnectingVdc.getLabel());
}
if (resp2.getCompatible() == false) {
log.error("The local vdc {} has been disconnected", myVdcId);
throw GeoException.fatals.disconnectVdcInvalidStatus(myVdc.getLabel());
}
}
}
private void disconnectVdc() {
updateOpStatus(ConnectionStatus.DISCONNECTING);
failedVdcStatus = ConnectionStatus.DISCONNECT_FAILED;
log.info("Disconnect vdc operation start ...");
removeVdcNodesFromCassandra();
log.info("Remove the vdc {} from cassandra node list", operatedVdc);
removeVdcFromStrategyOption(true);
log.info("Remove the vdc {} from cassandra strategy options", operatedVdc);
updateVdcStatus(ConnectionStatus.DISCONNECTED, false);
log.info("Set the vdc {} status to DISCONNECT_VDC", operatedVdc);
}
private void removeVdcNodesFromCassandra() {
try {
dbClient.removeVdcNodes(operatedVdc);
dbClient.addVdcNodesToBlacklist(operatedVdc);
} catch (Exception e) {
log.error("Failed to remove nodes from GeoDB : {}", e);
throw GeoException.fatals.vdcStrategyFailed(e);
}
}
@Override
public VdcConfig.ConfigChangeType changeType() {
return VdcConfig.ConfigChangeType.DISCONNECT_VDC;
}
}