/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.infra;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.data.validation.IPv4Address;
import play.data.validation.IPv6Address;
import play.data.validation.Range;
import play.data.validation.Required;
import play.data.validation.Validation;
import play.mvc.Controller;
import plugin.StorageOsPlugin;
import util.BourneUtil;
import util.MessagesUtils;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.services.util.PlatformUtils;
import com.emc.vipr.client.ViPRSystemClient;
import com.emc.vipr.model.sys.ClusterInfo.ClusterState;
import com.emc.vipr.model.sys.ipreconfig.ClusterIpInfo;
import com.emc.vipr.model.sys.ipreconfig.ClusterIpv4Setting;
import com.emc.vipr.model.sys.ipreconfig.ClusterIpv6Setting;
import com.emc.vipr.model.sys.ipreconfig.ClusterNetworkReconfigStatus;
import com.google.gson.Gson;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.util.FlashException;
@Restrictions({ @Restrict("SECURITY_ADMIN"), @Restrict("RESTRICTED_SECURITY_ADMIN") })
public class ClusterInfo extends Controller {
public static final String DEFAULT_IPV4_ADDR = "0.0.0.0";
public static final String DEFAULT_NETMASK = "255.255.255.0";
public static final String DEFAULT_IPV6_ADDR = "::0";
public static final int DEFAULT_PREFIX_LEN = 64;
private static String SUCCESS_KEY = "ipreconfig.successful";
private static String ERROR_KEY = "ipreconfig.error";
private static String EXCEPTION_KEY = "ipreconfig.exception";
private static String UNSUPPORTED_KEY = "ipReconfig.notSupported";
private static String UNSTABLE_KEY = "ipReconfig.clusterNotStable";
private static String RECONFIGURATION_STATUS_SUCCESS = "ipreconfig.status.success";
private static String RECONFIGURATION_STATUS_ERROR = "ipreconfig.status.error";
public static String vip = null;
/**
* loads render args
*/
private static void loadRenderArgs() {
boolean reconfigSupported = true;
boolean unsupportedPlatform = BourneUtil.getViprClient().vdcs().isGeoSetup();
if (unsupportedPlatform) {
reconfigSupported = false;
Logger.info(UNSUPPORTED_KEY + " isGeo: " + unsupportedPlatform);
flash.put("warning", MessagesUtils.get(UNSUPPORTED_KEY));
}
ClusterState clusterState = getClusterStateFromCoordinator();
if (clusterState == null || !clusterState.equals(ClusterState.STABLE)) {
reconfigSupported = false;
flash.put("warning", MessagesUtils.get(UNSTABLE_KEY));
}
renderArgs.put("reconfigSupported", reconfigSupported);
renderArgs.put("vip", vip);
}
/**
* loads cluster ip configuration details
*/
public static void clusterIpInfo(ClusterIpInfoForm ipReconfigForm) {
if (ipReconfigForm == null) {
ViPRSystemClient client = BourneUtil.getSysClient();
ClusterIpInfo clusterIpInfo = client.control().getClusterIpinfo();
ipReconfigForm = new ClusterIpInfoForm();
ipReconfigForm.load(clusterIpInfo);
ClusterNetworkReconfigStatus ipReconfigStatus = client.control().getClusterIpReconfigStatus();
if (ipReconfigStatus != null && ipReconfigStatus.getStatus() != null) {
if (ipReconfigStatus.getStatus().equals(ClusterNetworkReconfigStatus.Status.FAILED)) {
flash.error(MessagesUtils.get(RECONFIGURATION_STATUS_ERROR, ipReconfigStatus.getMessage()));
} else if (ipReconfigStatus.getStatus().equals(ClusterNetworkReconfigStatus.Status.SUCCEED)) {
if (ipReconfigStatus.isRecentlyReconfigured()) {
flash.put("info", MessagesUtils.get(RECONFIGURATION_STATUS_SUCCESS));
}
}
}
}
vip = ipReconfigForm.selectVipforStatusQuery();// NOSONAR
// ("Suppressing Sonar violation of Lazy initialization of static fields should be synchronized for vip. vip only fetches network info. Sync not needed.")
loadRenderArgs();
render(ipReconfigForm);
}
/**
* gets ip reconfigurations status in json format
*/
@FlashException()
public static void ipReconfigStatusJson() {
ViPRSystemClient client = BourneUtil.getSysClient();
ClusterNetworkReconfigStatus ipReconfigStatus = client.control().getClusterIpReconfigStatus();
Gson gson = new Gson();
String ipReconfigStatusJSON = gson.toJson(ipReconfigStatus);
renderJSON(ipReconfigStatusJSON);
}
/**
* Submit cluster IPs reconfiguration requests.
*
* @param ipReconfigForm
*/
public static void ipReconfig(final ClusterIpInfoForm ipReconfigForm) {
ipReconfigForm.validate();
if (Validation.hasErrors()) {
params.flash();
Validation.keep();
clusterIpInfo(ipReconfigForm);
}
final ViPRSystemClient client = BourneUtil.getSysClient();
final ClusterIpInfo clusterIpInfo = ipReconfigForm.getClusterIpInfo();
try {
boolean isAccepted = client.control().reconfigClusterIps(clusterIpInfo, ipReconfigForm.powerOff);
if (isAccepted) {
flash.put("info", MessagesUtils.get(SUCCESS_KEY));
} else {
flash.error(MessagesUtils.get(ERROR_KEY));
}
} catch (Exception e) {
flash.error(MessagesUtils.get(EXCEPTION_KEY, e.getMessage()));
Logger.error(e, e.getMessage());
}
clusterIpInfo(ipReconfigForm);
}
private static ClusterState getClusterStateFromCoordinator() {
if (StorageOsPlugin.isEnabled()) {
CoordinatorClient coordinatorClient = StorageOsPlugin.getInstance().getCoordinatorClient();
ClusterState clusterState = coordinatorClient.getControlNodesState();
return clusterState;
}
return null;
}
/**
* If it is a VMware app, then N/W re-configure should be available.
*
* @return Returns true if N/W re-configure should be available.
*/
public static boolean isVMwareVapp() {
boolean isEnabled = false;
try {
if (!PlatformUtils.isVMwareVapp()) {
isEnabled = true;
}
} catch (IllegalStateException ise) {
// Thrown if method could not determine platform.
Logger.warn("Could not determine platform.");
}
return isEnabled;
}
/**
* If it is a DR Multi-sites, then N/W re-configure should not be available.
*
* @return Returns true if for DR Multi-sites.
*/
public static boolean isDRMultisites() {
boolean isEnabled = false;
try {
if (PlatformUtils.hasMultipleSites()) {
isEnabled = true;
}
} catch (IllegalStateException ise) {
// Thrown if method could not determine platform.
Logger.warn("Could not Disaster Recovery environments.");
}
return isEnabled;
}
// "Suppressing Sonar violation of Field names should comply with naming convention"
@SuppressWarnings("squid:S00116")
public static class ClusterIpInfoForm {
@Required
public int nodeCount;
@Required
public boolean powerOff;
@Required
@IPv4Address
public String network_vip;
@Required
@IPv4Address
public String ipv4_network_addrs1;
@IPv4Address
public String ipv4_network_addrs2;
@IPv4Address
public String ipv4_network_addrs3;
@IPv4Address
public String ipv4_network_addrs4;
@IPv4Address
public String ipv4_network_addrs5;
@Required
@IPv4Address
public String network_netmask;
@Required
@IPv4Address
public String network_gateway;
@Required
@IPv6Address
public String network_vip6;
@Required
@IPv6Address
public String ipv6_network_addrs1;
@IPv6Address
public String ipv6_network_addrs2;
@IPv6Address
public String ipv6_network_addrs3;
@IPv6Address
public String ipv6_network_addrs4;
@IPv6Address
public String ipv6_network_addrs5;
@Required
@Range(min = 1, max = 128)
public int network_prefix_length;
@Required
@IPv6Address
public String network_gateway6;
public ClusterIpInfoForm() {
}
/**
* loads domain object from the form object
*
* @return
*/
public ClusterIpInfo getClusterIpInfo() {
ClusterIpInfo clusterIpInfo = new ClusterIpInfo();
ClusterIpv4Setting ipv4_setting = new ClusterIpv4Setting();
ipv4_setting.setNetworkVip(network_vip.trim());
ipv4_setting.setNetworkGateway(network_gateway.trim());
ipv4_setting.setNetworkNetmask(network_netmask.trim());
List<String> network_addrs = new ArrayList<String>();
network_addrs.add(ipv4_network_addrs1.trim());
if (!StringUtils.isEmpty(ipv4_network_addrs2)) {
network_addrs.add(ipv4_network_addrs2.trim());
}
if (!StringUtils.isEmpty(ipv4_network_addrs3)) {
network_addrs.add(ipv4_network_addrs3.trim());
}
if (!StringUtils.isEmpty(ipv4_network_addrs4)) {
network_addrs.add(ipv4_network_addrs4.trim());
}
if (!StringUtils.isEmpty(ipv4_network_addrs5)) {
network_addrs.add(ipv4_network_addrs5.trim());
}
ipv4_setting.setNetworkAddrs(network_addrs);
ClusterIpv6Setting ipv6_setting = new ClusterIpv6Setting();
ipv6_setting.setNetworkVip6(network_vip6.trim());
ipv6_setting.setNetworkGateway6(network_gateway6.trim());
ipv6_setting.setNetworkPrefixLength(network_prefix_length);
List<String> network_addrs6 = new ArrayList<String>();
network_addrs6.add(ipv6_network_addrs1.trim());
if (!StringUtils.isEmpty(ipv6_network_addrs2)) {
network_addrs6.add(ipv6_network_addrs2.trim());
}
if (!StringUtils.isEmpty(ipv6_network_addrs3)) {
network_addrs6.add(ipv6_network_addrs3.trim());
}
if (!StringUtils.isEmpty(ipv6_network_addrs4)) {
network_addrs6.add(ipv6_network_addrs4.trim());
}
if (!StringUtils.isEmpty(ipv6_network_addrs5)) {
network_addrs6.add(ipv6_network_addrs5.trim());
}
ipv6_setting.setNetworkAddrs(network_addrs6);
clusterIpInfo.setIpv4Setting(ipv4_setting);
clusterIpInfo.setIpv6Setting(ipv6_setting);
return clusterIpInfo;
}
/**
* load form from the domain object
*
* @param getClusterIpInfo
*/
public void load(ClusterIpInfo getClusterIpInfo) {
if (getClusterIpInfo != null) {
if (getClusterIpInfo.getIpv4Setting() != null) {
network_vip = getClusterIpInfo.getIpv4Setting().getNetworkVip();
network_netmask = getClusterIpInfo.getIpv4Setting().getNetworkNetmask();
network_gateway = getClusterIpInfo.getIpv4Setting().getNetworkGateway();
if (getClusterIpInfo.getIpv4Setting().getNetworkAddrs().size() >= 1) { // NOSONAR
// ("Suppressing Sonar violation of Use isEmpty() to check whether the collection is empty. No empty check required.")
ipv4_network_addrs1 = getClusterIpInfo.getIpv4Setting().getNetworkAddrs().get(0);
}
if (getClusterIpInfo.getIpv4Setting().getNetworkAddrs().size() >= 2) {
ipv4_network_addrs2 = getClusterIpInfo.getIpv4Setting().getNetworkAddrs().get(1);
}
if (getClusterIpInfo.getIpv4Setting().getNetworkAddrs().size() >= 3) {
ipv4_network_addrs3 = getClusterIpInfo.getIpv4Setting().getNetworkAddrs().get(2);
}
if (getClusterIpInfo.getIpv4Setting().getNetworkAddrs().size() >= 4) {
ipv4_network_addrs4 = getClusterIpInfo.getIpv4Setting().getNetworkAddrs().get(3);
}
if (getClusterIpInfo.getIpv4Setting().getNetworkAddrs().size() >= 5) {
ipv4_network_addrs5 = getClusterIpInfo.getIpv4Setting().getNetworkAddrs().get(4);
}
if (ipv4_network_addrs4 != null && !ipv4_network_addrs4.equals(DEFAULT_IPV4_ADDR)) {
this.nodeCount = 5;
} else if (ipv4_network_addrs2 != null && !ipv4_network_addrs2.equals(DEFAULT_IPV4_ADDR)) {
this.nodeCount = 3;
} else {
this.nodeCount = 1;
}
} else {
loadIpv4SettingsDefaults();
}
if (getClusterIpInfo.getIpv6Setting() != null) {
network_vip6 = getClusterIpInfo.getIpv6Setting().getNetworkVip6();
network_prefix_length = getClusterIpInfo.getIpv6Setting().getNetworkPrefixLength();
network_gateway6 = getClusterIpInfo.getIpv6Setting().getNetworkGateway6();
if (getClusterIpInfo.getIpv6Setting().getNetworkAddrs().size() >= 1) { // NOSONAR
// ("Suppressing Sonar violation of Use isEmpty() to check whether the collection is empty. No empty check required.")
ipv6_network_addrs1 = getClusterIpInfo.getIpv6Setting().getNetworkAddrs().get(0);
}
if (getClusterIpInfo.getIpv6Setting().getNetworkAddrs().size() >= 2) {
ipv6_network_addrs2 = getClusterIpInfo.getIpv6Setting().getNetworkAddrs().get(1);
}
if (getClusterIpInfo.getIpv6Setting().getNetworkAddrs().size() >= 3) {
ipv6_network_addrs3 = getClusterIpInfo.getIpv6Setting().getNetworkAddrs().get(2);
}
if (getClusterIpInfo.getIpv6Setting().getNetworkAddrs().size() >= 4) {
ipv6_network_addrs4 = getClusterIpInfo.getIpv6Setting().getNetworkAddrs().get(3);
}
if (getClusterIpInfo.getIpv6Setting().getNetworkAddrs().size() >= 5) {
ipv6_network_addrs5 = getClusterIpInfo.getIpv6Setting().getNetworkAddrs().get(4);
}
int v6NodeCount = 0;
if (ipv6_network_addrs4 != null && !ipv6_network_addrs4.equals(DEFAULT_IPV6_ADDR)) {
v6NodeCount = 5;
} else if (ipv6_network_addrs2 != null && !ipv6_network_addrs2.equals(DEFAULT_IPV6_ADDR)) {
v6NodeCount = 3;
} else {
v6NodeCount = 1;
}
if (v6NodeCount > this.nodeCount) {
this.nodeCount = v6NodeCount;
}
} else {
loadIpv6SettingsDefaults();
}
}
}
public void loadIpv4SettingsDefaults() {
network_vip = DEFAULT_IPV4_ADDR;
network_netmask = DEFAULT_NETMASK;
network_gateway = DEFAULT_IPV4_ADDR;
ipv4_network_addrs1 = DEFAULT_IPV4_ADDR;
ipv4_network_addrs2 = DEFAULT_IPV4_ADDR;
ipv4_network_addrs3 = DEFAULT_IPV4_ADDR;
}
public void loadIpv6SettingsDefaults() {
network_vip6 = DEFAULT_IPV6_ADDR;
network_prefix_length = DEFAULT_PREFIX_LEN;
network_gateway6 = DEFAULT_IPV6_ADDR;
ipv6_network_addrs1 = DEFAULT_IPV6_ADDR;
ipv6_network_addrs2 = DEFAULT_IPV6_ADDR;
ipv6_network_addrs3 = DEFAULT_IPV6_ADDR;
}
/**
* alternate/custom validations
*/
public void validate() {
Validation.valid("ipReconfigForm", this);
if (nodeCount >= 3) {
Validation.required("ipReconfigForm.ipv4_network_addrs2", this.ipv4_network_addrs2);
Validation.required("ipReconfigForm.ipv6_network_addrs2", this.ipv6_network_addrs2);
Validation.required("ipReconfigForm.ipv4_network_addrs3", this.ipv4_network_addrs3);
Validation.required("ipReconfigForm.ipv6_network_addrs3", this.ipv6_network_addrs3);
}
if (nodeCount == 5) {
Validation.required("ipReconfigForm.ipv4_network_addrs4", this.ipv4_network_addrs4);
Validation.required("ipReconfigForm.ipv6_network_addrs4", this.ipv6_network_addrs4);
Validation.required("ipReconfigForm.ipv4_network_addrs5", this.ipv4_network_addrs5);
Validation.required("ipReconfigForm.ipv6_network_addrs5", this.ipv6_network_addrs5);
}
ClusterIpInfo ipInfo = this.getClusterIpInfo();
if (ipInfo != null && ipInfo.getIpv4Setting() != null && ipInfo.getIpv6Setting() != null) {
if (ipInfo.getIpv4Setting().isDefault() && ipInfo.getIpv6Setting().isDefault()) {
Validation.addError(null, "validation.noConfiguration");
}
}
if (ipInfo != null) { // NOSONAR ("Suppressing Sonar violation of Redundant null check of ipInfo�)
if (ipInfo.getIpv4Setting() != null && !ipInfo.getIpv4Setting().isDefault()) {
if (!ipInfo.getIpv4Setting().isOnSameNetworkIPv4()) {
Validation.addError(null, "validation.notOnSameNwIpv4");
}
}
if (ipInfo.getIpv6Setting() != null && !ipInfo.getIpv6Setting().isDefault()) {
if (!ipInfo.getIpv6Setting().isOnSameNetworkIPv6()) {
Validation.addError(null, "validation.notOnSameNwIpv6");
}
}
}
Set<String> dupValidationSet = new HashSet<String>();
if (network_vip != DEFAULT_IPV4_ADDR) {
dupValidationSet.add(network_vip);
if (isDuplicate(dupValidationSet, ipv4_network_addrs1)) {
Validation.addError("ipReconfigForm.ipv4_network_addrs1", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv4_network_addrs2)) {
Validation.addError("ipReconfigForm.ipv4_network_addrs2", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv4_network_addrs3)) {
Validation.addError("ipReconfigForm.ipv4_network_addrs3", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv4_network_addrs4)) {
Validation.addError("ipReconfigForm.ipv4_network_addrs4", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv4_network_addrs5)) {
Validation.addError("ipReconfigForm.ipv4_network_addrs5", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, network_gateway)) {
Validation.addError("ipReconfigForm.network_gateway", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, network_netmask)) {
Validation.addError("ipReconfigForm.network_netmask", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, network_vip6)) {
Validation.addError("ipReconfigForm.network_vip6", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv6_network_addrs1)) {
Validation.addError("ipReconfigForm.ipv6_network_addrs1", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv6_network_addrs2)) {
Validation.addError("ipReconfigForm.ipv6_network_addrs2", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv6_network_addrs3)) {
Validation.addError("ipReconfigForm.ipv6_network_addrs3", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv6_network_addrs4)) {
Validation.addError("ipReconfigForm.ipv6_network_addrs4", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, ipv6_network_addrs5)) {
Validation.addError("ipReconfigForm.ipv6_network_addrs5", "validation.duplicateIpAddress");
}
if (isDuplicate(dupValidationSet, network_gateway6)) {
Validation.addError("ipReconfigForm.network_gateway6", "validation.duplicateIpAddress");
}
}
}
private boolean isDuplicate(Set<String> dupValidationSet, String ip) {
boolean isDuplicate = false;
if (!StringUtils.isEmpty(ip) && (!ip.equals(DEFAULT_IPV4_ADDR) && !ip.equals(DEFAULT_IPV6_ADDR))) {
int expectedSize = dupValidationSet.size() + 1;
dupValidationSet.add(ip);
if (dupValidationSet.size() < expectedSize) {
isDuplicate = true;
}
}
return isDuplicate;
}
private String selectVipforStatusQuery() {
if (!StringUtils.isEmpty(network_vip) && !network_vip.equals(DEFAULT_IPV4_ADDR)) {
return network_vip;
} else {
return network_vip6;
}
}
}
}