/*
* Copyright (c) 2014-2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.util;
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 org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.coordinator.client.model.Site;
import com.emc.storageos.coordinator.client.model.SiteInfo;
import com.emc.storageos.coordinator.client.model.SiteState;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.coordinator.client.service.DrUtil;
import com.emc.storageos.coordinator.exceptions.RetryableCoordinatorException;
/**
* Utility class to generate VDC/Site property for syssvc.
*
* The VDC/Site configurations are stored in ZK as follows:
* /config/disasterRecoverySites/<vdc_short_id>/<site_uuid> has all the VDC/site configurations
* /config/disasterRecoveryActive/<vdc_short_id> specifies which site is the active in each VDC
* /config/geoLocalVDC/global specifies the local VDC in the geo federation
*
* The vdcconfig.properties includes the node IPs as the following
* vdc_vdc*_site*_node_count(e.g vdc_vdc1_site1_node_count) - number of nodes in specified site of specified vdc
* vdc_vdc*_site*_network_*_ipaddr(e.g vdc_vdc1_site1_network_1_ipaddr) - IPv4 address of specified node in specified site of specified vdc
* vdc_vdc*_site*_network_*_vip - VIP of specific site of specified vdc
* vdc_vdc*_site*_network_*_ipaddr6 - IPv6 address of specified node in specified site of specified vdc
* vdc_vdc*_site*_network_vip6 - IPv6 VIP specific site of specified vdc
* vdc_ids - all vdc ids (e.g vdc1, vdc2 .. )
* vdc_myid - current vdc short id
* site_ids - all site ids in current vdc
* vdc_vdc*_site_ids - site ids of specified vdc
* site_active_id - active site id in current vdc
* site_myid - current site id
*
* back_compat_preyoda - true/false. If it is upgraded from pre-yoda, we set it true so dbsvc/syssvc may keep
* some features(e.g ssl encryption) for backward compatibility
* vdc_config_version - timestamp for last vdc config change. It should be same for all instance in GEO/DR
*/
public class VdcConfigUtil {
private static final String DEFAULT_ACTIVE_SITE_ID = "site1";
private static final Logger log = LoggerFactory.getLogger(VdcConfigUtil.class);
public static final String VDC_CONFIG_VERSION = "vdc_config_version";
public static final String VDC_MYID = "vdc_myid";
public static final String VDC_IDS = "vdc_ids";
public static final String VDC_SITE_NODE_COUNT_PTN = "vdc_%s_%s_node_count";
public static final String VDC_SITE_IPADDR6_PTN = "vdc_%s_%s_network_%d_ipaddr6";
public static final String VDC_SITE_IPADDR_PTN = "vdc_%s_%s_network_%d_ipaddr";
public static final String VDC_SITE_VIP_PTN = "vdc_%s_%s_network_vip";
public static final String VDC_SITE_VIP6_PTN = "vdc_%s_%s_network_vip6";
public static final String SITE_IS_STANDBY="site_is_standby";
public static final String SITE_MY_UUID="site_my_uuid";
public static final String SITE_MYID="site_myid";
public static final String SITE_IDS="site_ids";
public static final String VDC_SITE_IDS="vdc_%s_site_ids";
public static final String SITE_ACTIVE_ID="site_active_id";
public static final String BACK_COMPAT_PREYODA="back_compat_preyoda";
private DrUtil drUtil;
private Boolean backCompatPreYoda = false;
private CoordinatorClient coordinator;
public VdcConfigUtil(CoordinatorClient coordinator) {
drUtil = new DrUtil(coordinator);
this.coordinator = coordinator;
}
public void setBackCompatPreYoda(Boolean backCompatPreYoda) {
this.backCompatPreYoda = backCompatPreYoda;
}
/**
* generates a property map containing all the VDC/site information this VDC has in
* ZK, to be used by syssvc to update the local system property.
*
* @return a map containing VDC/site configs
*/
public Map<String, String> genVdcProperties() {
Map<String, String> vdcConfig = new HashMap<>();
Map<String, List<Site>> vdcSiteMap = drUtil.getVdcSiteMap();
if (vdcSiteMap.isEmpty()) {
log.warn("No virtual data center defined in ZK");
throw new IllegalStateException("No virtual data center defined in ZK");
}
vdcConfig.put(VDC_MYID, drUtil.getLocalVdcShortId());
List<String> vdcShortIdList = new ArrayList<>(vdcSiteMap.keySet());
for (String vdcShortId : vdcShortIdList) {
genSiteProperties(vdcConfig, vdcShortId, vdcSiteMap.get(vdcShortId));
}
// sort the vdc short ids by their indices, note that vdc11 should be greater
// than vdc2
Collections.sort(vdcShortIdList, new Comparator<String>() {
@Override
public int compare(String left, String right) {
Integer leftInt = Integer.valueOf(left.substring(3));
Integer rightInt = Integer.valueOf(right.substring(3));
return leftInt.compareTo(rightInt);
}
});
vdcConfig.put(VDC_IDS, StringUtils.join(vdcShortIdList, ","));
vdcConfig.put(BACK_COMPAT_PREYODA, String.valueOf(backCompatPreYoda));
log.info("vdc config property: \n{}", vdcConfig.toString());
return vdcConfig;
}
private void genSiteProperties(Map<String, String> vdcConfig, String vdcShortId, List<Site> sites) {
String activeSiteId = null;
try {
activeSiteId = drUtil.getActiveSite().getUuid();
} catch (RetryableCoordinatorException e) {
log.warn("Failed to find active site id from ZK, go on since it maybe switchover case");
}
SiteInfo siteInfo = coordinator.getTargetInfo(SiteInfo.class);
Site localSite = drUtil.getLocalSite();
if (StringUtils.isEmpty(activeSiteId) && SiteInfo.DR_OP_SWITCHOVER.equals(siteInfo.getActionRequired())) {
activeSiteId = drUtil.getSiteFromLocalVdc(siteInfo.getTargetSiteUUID()).getUuid();
}
Collections.sort(sites, new Comparator<Site>() {
@Override
public int compare(Site a, Site b) {
return (int)(a.getCreationTime() - b.getCreationTime());
}
});
List<String> shortIds = new ArrayList<>();
for (Site site : sites) {
if (shouldExcludeFromConfig(site)) {
log.info("Ignore site {} of vdc {}", site.getSiteShortId(), site.getVdcShortId());
continue;
}
// exclude the paused sites from the standby site list on every site except the paused site
// this will make it easier to resume the data replication.
if (!drUtil.isLocalSite(site)
&& (site.getState().equals(SiteState.STANDBY_PAUSING)
|| site.getState().equals(SiteState.STANDBY_PAUSED)
|| site.getState().equals(SiteState.STANDBY_REMOVING)
|| site.getState().equals(SiteState.ACTIVE_FAILING_OVER)
|| site.getState().equals(SiteState.ACTIVE_DEGRADED))) {
continue;
}
int siteNodeCnt = 0;
Map<String, String> siteIPv4Addrs = site.getHostIPv4AddressMap();
Map<String, String> siteIPv6Addrs = site.getHostIPv6AddressMap();
List<String> siteHosts = getHostsFromIPAddrMap(siteIPv4Addrs, siteIPv6Addrs);
String siteShortId = site.getSiteShortId();
// sort the host names as vipr1, vipr2 ...
Collections.sort(siteHosts);
for (String hostName : siteHosts) {
siteNodeCnt++;
String address = siteIPv4Addrs.get(hostName);
vdcConfig.put(String.format(VDC_SITE_IPADDR_PTN, vdcShortId, siteShortId, siteNodeCnt),
address == null ? "" : address);
address = siteIPv6Addrs.get(hostName);
vdcConfig.put(String.format(VDC_SITE_IPADDR6_PTN, vdcShortId, siteShortId, siteNodeCnt),
address == null ? "" : address);
}
vdcConfig.put(String.format(VDC_SITE_NODE_COUNT_PTN, vdcShortId, siteShortId),
String.valueOf(siteNodeCnt));
vdcConfig.put(String.format(VDC_SITE_VIP_PTN, vdcShortId, siteShortId), site.getVip());
vdcConfig.put(String.format(VDC_SITE_VIP6_PTN, vdcShortId, siteShortId), site.getVip6());
if (drUtil.isLocalSite(site)) {
vdcConfig.put(SITE_MYID, siteShortId);
vdcConfig.put(SITE_MY_UUID, site.getUuid());
}
shortIds.add(siteShortId);
}
Collections.sort(shortIds);
if (drUtil.getLocalVdcShortId().equals(vdcShortId)) {
// right now we assume that SITE_IDS and SITE_IS_STANDBY only makes sense for local VDC
// moving forward this may or may not be the case.
vdcConfig.put(SITE_IDS, StringUtils.join(shortIds, ','));
vdcConfig.put(SITE_IS_STANDBY, String.valueOf(!localSite.getUuid().equals(activeSiteId)));
vdcConfig.put(SITE_ACTIVE_ID, StringUtils.isEmpty(activeSiteId) ?
DEFAULT_ACTIVE_SITE_ID :
drUtil.getSiteFromLocalVdc(activeSiteId).getSiteShortId());
}
vdcConfig.put(String.format(VDC_SITE_IDS, vdcShortId), StringUtils.join(shortIds, ','));
}
private List<String> getHostsFromIPAddrMap(Map<String, String> IPv4Addresses, Map<String, String> IPv6Addresses) {
List<String> hostNameListV4 = new ArrayList<>(IPv4Addresses.keySet());
List<String> hostNameListV6 = new ArrayList<>(IPv6Addresses.keySet());
List<String> hostNameList = hostNameListV4;
if (hostNameListV4.isEmpty()) {
hostNameList = hostNameListV6;
}
return hostNameList;
}
/**
* Return true to indicate current site need be excluded in vdc config properties
*
* @param site
* @return
*/
private boolean shouldExcludeFromConfig(Site site) {
// No node ip available in the site config
return site.getNodeCount() < 1;
}
}