/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.infra;
import com.emc.storageos.coordinator.client.model.SiteState;
import com.emc.storageos.model.dr.SiteActive;
import com.emc.storageos.model.dr.SiteAddParam;
import com.emc.storageos.model.dr.SiteDetailRestRep;
import com.emc.storageos.model.dr.SiteErrorResponse;
import com.emc.storageos.model.dr.SiteIdListParam;
import com.emc.storageos.model.dr.SiteRestRep;
import com.emc.storageos.model.dr.SiteUpdateParam;
import com.emc.vipr.client.exceptions.ServiceErrorException;
import com.emc.vipr.model.sys.ClusterInfo;
import com.google.common.collect.Lists;
import controllers.Common;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.util.FlashException;
import controllers.util.ViprResourceController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import models.datatable.DisasterRecoveryDataTable;
import models.datatable.DisasterRecoveryDataTable.StandByInfo;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import play.data.binding.As;
import play.data.validation.MaxSize;
import play.data.validation.Required;
import play.data.validation.Validation;
import play.mvc.With;
import util.DisasterRecoveryUtils;
import util.MessagesUtils;
import util.datatable.DataTablesSupport;
import util.validation.HostNameOrIpAddress;
@With(Common.class)
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN"), @Restrict("SYSTEM_MONITOR"),
@Restrict("SYSTEM_ADMIN"), @Restrict("RESTRICTED_SYSTEM_ADMIN")})
public class DisasterRecovery extends ViprResourceController {
protected static final String SAVED_SUCCESS = "disasterRecovery.save.success";
protected static final String PAUSED_SUCCESS = "disasterRecovery.pause.success";
protected static final String PAUSED_ERROR = "disasterRecovery.pause.error";
protected static final String SWITCHOVER_SUCCESS = "disasterRecovery.switchover.success";
protected static final String SWITCHOVER_ERROR = "disasterRecovery.switchover.error";
protected static final String RESUMED_SUCCESS = "disasterRecovery.resume.success";
protected static final String RETRY_SUCCESS = "disasterRecovery.retry.success";
protected static final String SAVED_ERROR = "disasterRecovery.save.error";
protected static final String DELETED_SUCCESS = "disasterRecovery.delete.success";
protected static final String DELETED_ERROR = "disasterRecovery.delete.error";
protected static final String UNKNOWN = "disasterRecovery.unknown";
protected static final String UPDATE_SUCCESS = "disasterRecovery.update.success";
protected static final String ADD_WARNING = "disasterRecovery.add.unstable.warning";
// We don't support failover to site of ACTIVE_DEGRADED state in X-wing , thus no need to show network and data-sync status.
// So re-add ACTIVE_DEGRADED state back to this list to hide these details for site of this state as same as in Yoda.
private static final List<SiteState> activeStates =
Arrays.asList(SiteState.ACTIVE, SiteState.ACTIVE_DEGRADED, SiteState.ACTIVE_FAILING_OVER, SiteState.ACTIVE_SWITCHING_OVER);
private static void backToReferrer() {
String referrer = Common.getReferrer();
if (StringUtils.isNotBlank(referrer)) {
redirect(referrer);
}
else {
list();
}
}
private static void list() {
list(false);
}
public static void list(boolean showPauseButton) {
DisasterRecoveryDataTable dataTable = createDisasterRecoveryDataTable();
String localSiteUuid = DisasterRecoveryUtils.getLocalUuid();
render(dataTable, showPauseButton, localSiteUuid);
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN"), @Restrict("SYSTEM_ADMIN"),
@Restrict("RESTRICTED_SYSTEM_ADMIN") })
public static void pause(@As(",") String[] ids) {
List<String> uuids = Arrays.asList(ids);
for (String uuid : uuids) {
if (!DisasterRecoveryUtils.hasStandbySite(uuid)) {
flash.error(MessagesUtils.get(UNKNOWN, uuid));
list(true);
}
}
SiteIdListParam param = new SiteIdListParam();
param.getIds().addAll(uuids);
try {
DisasterRecoveryUtils.pauseStandby(param);
} catch (ServiceErrorException ex) {
flash.error(ex.getDetailedMessage());
list(true);
} catch (Exception ex) {
flash.error(ex.getMessage());
list(true);
}
flash.success(MessagesUtils.get(PAUSED_SUCCESS));
list(true);
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN"), @Restrict("SYSTEM_ADMIN"),
@Restrict("RESTRICTED_SYSTEM_ADMIN") })
public static void resume(String id) {
SiteRestRep result = DisasterRecoveryUtils.getSite(id);
if (result != null) {
SiteRestRep siteresume = DisasterRecoveryUtils.resumeStandby(id);
flash.success(MessagesUtils.get(RESUMED_SUCCESS, siteresume.getName()));
}
list();
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN"), @Restrict("SYSTEM_ADMIN"),
@Restrict("RESTRICTED_SYSTEM_ADMIN") })
public static void retry(String id) {
SiteRestRep result = DisasterRecoveryUtils.getSite(id);
if (result != null) {
SiteRestRep siteretry = DisasterRecoveryUtils.retryStandby(id);
if (siteretry.getState().equals(SiteState.STANDBY_FAILING_OVER.name())){
String standby_name = siteretry.getName();
String standby_vip = siteretry.getVipEndpoint();
String active_name = null;
for (SiteRestRep site: DisasterRecoveryUtils.getStandbySites()){
if (site.getState().equals(SiteState.ACTIVE_FAILING_OVER.name()) ||
site.getState().equals(SiteState.ACTIVE_DEGRADED.name())){
active_name = site.getName();
break;
}
}
Boolean isSwitchover = false;
String site_uuid = id;
maintenance(active_name, standby_name, standby_vip, site_uuid, isSwitchover);
}
else {
flash.success(MessagesUtils.get(RETRY_SUCCESS, siteretry.getName()));
list();
}
}
else {
flash.error(MessagesUtils.get(UNKNOWN, id));
list();
}
}
public static void test(String id) {
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void switchover(String id) {
String standby_name = null;
String standby_vip = null;
String active_name = null;
Boolean isSwitchover = false;
// Get active site details
SiteRestRep activesite = DisasterRecoveryUtils.getActiveSite();
active_name = activesite == null ? "N/A" : activesite.getName();
SiteRestRep result = DisasterRecoveryUtils.getSite(id);
if (result != null) {
// Check Switchover or Failover
SiteActive currentSite = DisasterRecoveryUtils.checkActiveSite();
if (currentSite.getIsActive() == true) {
DisasterRecoveryUtils.doSwitchover(id);
isSwitchover = true;
}
else {
DisasterRecoveryUtils.doFailover(id);
isSwitchover = false;
}
standby_name = result.getName();
standby_vip = result.getVipEndpoint();
}
String site_uuid = id;
maintenance(active_name, standby_name, standby_vip, site_uuid, isSwitchover);
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void maintenance(String active_name, String standby_name, String standby_vip, String site_uuid, Boolean isSwitchover) {
render(active_name, standby_name, standby_vip, site_uuid, isSwitchover);
}
private static DisasterRecoveryDataTable createDisasterRecoveryDataTable() {
DisasterRecoveryDataTable dataTable = new DisasterRecoveryDataTable();
return dataTable;
}
public static void listJson() {
List<DisasterRecoveryDataTable.StandByInfo> disasterRecoveries = Lists.newArrayList();
for (SiteRestRep siteConfig : DisasterRecoveryUtils.getSites()) {
disasterRecoveries.add(new StandByInfo(siteConfig));
}
renderJSON(DataTablesSupport.createJSON(disasterRecoveries, params));
}
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void create() {
for (SiteRestRep site : DisasterRecoveryUtils.getSites()) {
if (SiteState.STANDBY_PAUSED.toString().equals(site.getState())) {
continue;
}
SiteDetailRestRep detail = DisasterRecoveryUtils.getSiteDetails(site.getUuid());
if (!ClusterInfo.ClusterState.STABLE.toString().equals(detail.getClusterState())) {
flash.error(MessagesUtils.get(ADD_WARNING, site.getName()));
list();
}
}
DisasterRecoveryForm site = new DisasterRecoveryForm();
edit(site);
}
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void edit(String id) {
SiteRestRep siteRest = DisasterRecoveryUtils.getSite(id);
if (siteRest != null) {
DisasterRecoveryForm disasterRecovery = new DisasterRecoveryForm(siteRest);
edit(disasterRecovery);
}
else {
flash.error(MessagesUtils.get(UNKNOWN, id));
list();
}
}
private static void edit(DisasterRecoveryForm disasterRecovery) {
render("@edit", disasterRecovery);
}
@FlashException(keep = true, referrer = { "create", "edit" })
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void save(DisasterRecoveryForm disasterRecovery) {
if (disasterRecovery != null) {
disasterRecovery.validate("disasterRecovery");
if (Validation.hasErrors()) {
Common.handleError();
}
if (disasterRecovery.isNew()) {
SiteAddParam standbySite = new SiteAddParam();
standbySite.setName(disasterRecovery.name);
standbySite.setVip(disasterRecovery.VirtualIP);
standbySite.setUsername(disasterRecovery.userName);
standbySite.setPassword(disasterRecovery.userPassword);
standbySite.setDescription(disasterRecovery.description);
SiteRestRep result = DisasterRecoveryUtils.addStandby(standbySite);
flash.success(MessagesUtils.get(SAVED_SUCCESS, result.getName()));
list();
}
else {
SiteUpdateParam siteUpdateParam = new SiteUpdateParam();
siteUpdateParam.setName(disasterRecovery.name);
siteUpdateParam.setDescription(disasterRecovery.description);
DisasterRecoveryUtils.updateSite(disasterRecovery.id, siteUpdateParam);
flash.success(MessagesUtils.get(UPDATE_SUCCESS, disasterRecovery.name));
list();
}
}
}
@FlashException("list")
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public static void delete(@As(",") String[] ids) {
List<String> uuids = Arrays.asList(ids);
for (String uuid : uuids) {
if (!DisasterRecoveryUtils.hasStandbySite(uuid)) {
flash.error(MessagesUtils.get(UNKNOWN, uuid));
list();
}
}
SiteIdListParam param = new SiteIdListParam();
param.getIds().addAll(uuids);
DisasterRecoveryUtils.deleteStandby(param);
flash.success(MessagesUtils.get(DELETED_SUCCESS));
list();
}
public static void itemsJson(@As(",") String[] ids) {
List<String> uuids = Arrays.asList(ids);
itemsJson(uuids);
}
public static boolean isActiveSite() {
return DisasterRecoveryUtils.isActiveSite();
}
public static boolean isLocalsiteRemoved() {
return DisasterRecoveryUtils.isLocalSiteRemoved();
}
public static boolean isMultiDrSite() {
return DisasterRecoveryUtils.isMultiDrSite();
}
public static boolean isRetrySite(String uuid) {
SiteErrorResponse error = DisasterRecoveryUtils.getSiteError(uuid);
if(!error.getOperation().equals(SiteState.STANDBY_PAUSING.name())
&& !error.getOperation().equals(SiteState.STANDBY_RESUMING.name())
&& !error.getOperation().equals(SiteState.STANDBY_FAILING_OVER.name())){
return false;
}
return true;
}
public static String getLocalSiteName() {
return DisasterRecoveryUtils.getLocalSiteName();
}
public static void checkFailoverProgress(String uuid) {
SiteRestRep siteRest = DisasterRecoveryUtils.getSite(uuid);
renderJSON(siteRest);
}
private static boolean isActiveSiteState(SiteState state) {
return activeStates.contains(state);
}
public static String getLocalSiteState() {
SiteRestRep site = DisasterRecoveryUtils.getLocalSite();
return site != null ? site.getState() : "";
}
public static void errorDetails(String id) {
Boolean isError = false;
String uuid = id;
// site id doesn't exist
if (!DisasterRecoveryUtils.hasStandbySite(id)) {
SiteDetailRestRep disasterSiteTime = new SiteDetailRestRep();
uuid = "Unknown Standby site id: " + id;
render(isError, uuid, disasterSiteTime);
}
SiteRestRep siteRest = DisasterRecoveryUtils.getSite(id);
// site is in STANDBY_ERROR state
if (siteRest.getState().equals(String.valueOf(SiteState.STANDBY_ERROR))) {
SiteErrorResponse disasterSiteError = DisasterRecoveryUtils.getSiteError(id);
isError = true;
if (disasterSiteError.getCreationTime() != null) {
DateTime errorCreationTime = new DateTime(disasterSiteError.getCreationTime().getTime());
renderArgs.put("errorCreationTime", errorCreationTime);
}
DateTime siteCreationTime = new DateTime(siteRest.getCreateTime());
renderArgs.put("siteCreationTime", siteCreationTime);
if (disasterSiteError.getOperation() != null) {
String operation = disasterSiteError.getOperation();
renderArgs.put("operation", operation);
}
render(isError, uuid, disasterSiteError);
}
SiteDetailRestRep disasterSiteDetails = DisasterRecoveryUtils.getSiteDetails(id);
Boolean isActive = isActiveSiteState(Enum.valueOf(SiteState.class, siteRest.getState()));
renderArgs.put("isActive", isActive);
if (disasterSiteDetails.getLastSyncTime() != null) {
DateTime lastSyncTime = new DateTime(disasterSiteDetails.getLastSyncTime().getTime());
renderArgs.put("lastSyncTime", lastSyncTime);
}
if (disasterSiteDetails.getCreationTime() != null) {
DateTime creationTime = new DateTime(disasterSiteDetails.getCreationTime().getTime());
renderArgs.put("creationTime", creationTime);
}
render(isError, uuid, disasterSiteDetails);
}
public static boolean hasPausedSite() {
return DisasterRecoveryUtils.hasPausedSite();
}
public static boolean hasActiveDegradedSite() {
return DisasterRecoveryUtils.hasActiveDegradedSite();
}
private static void itemsJson(List<String> uuids) {
List<SiteRestRep> standbySites = new ArrayList<SiteRestRep>();
for (String uuid : uuids) {
SiteRestRep standbySite = DisasterRecoveryUtils.getSite(uuid);
if (standbySite != null) {
standbySites.add(standbySite);
}
}
performItemsJson(standbySites, new JsonItemOperation());
}
protected static class JsonItemOperation implements ResourceValueOperation<StandByInfo, SiteRestRep> {
@Override
public StandByInfo performOperation(SiteRestRep provider) throws Exception {
return new StandByInfo(provider);
}
}
// Suppressing Sonar violation of Password Hardcoded. Password is not hardcoded here.
@SuppressWarnings("squid:S2068")
public static class DisasterRecoveryForm {
public String id;
@MaxSize(2048)
@Required
public String name;
@Required
@HostNameOrIpAddress
public String VirtualIP;
@MaxSize(2048)
public String userName;
@MaxSize(2048)
public String userPassword;
@MaxSize(2048)
public String confirmPassword;
@MaxSize(2048)
public String description;
public DisasterRecoveryForm() {
this.userPassword = "";
this.confirmPassword = "";
}
public DisasterRecoveryForm(SiteAddParam siteaddParam) {
this.id = siteaddParam.getId();
this.name = siteaddParam.getName();
this.userName = siteaddParam.getUsername();
this.VirtualIP = siteaddParam.getVip();
this.description = siteaddParam.getDescription();
}
public DisasterRecoveryForm(SiteRestRep siteeditParam) {
this.id = siteeditParam.getUuid();
this.name = siteeditParam.getName();
this.description = siteeditParam.getDescription();
this.VirtualIP = siteeditParam.getVipEndpoint();
}
public boolean isNew() {
return StringUtils.isBlank(id);
}
public void validate(String fieldName) {
if (isNew()) {
Validation.valid(fieldName, this);
Validation.required(fieldName + ".name", this.name);
Validation.required(fieldName + ".VirtualIP", this.VirtualIP);
Validation.required(fieldName + ".userName", this.userName);
Validation.required(fieldName + ".userPassword", this.userPassword);
Validation.required(fieldName + ".confirmPassword", this.confirmPassword);
if (!isMatchingPasswords(userPassword, confirmPassword)) {
Validation.addError(fieldName + ".confirmPassword",
MessagesUtils.get("storageArray.confirmPassword.not.match"));
}
}
}
private boolean isMatchingPasswords(String password, String confirm) {
return StringUtils.equals(StringUtils.trimToEmpty(password), StringUtils.trimToEmpty(confirm));
}
}
}