/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package controllers.arrays; import com.emc.storageos.model.systems.StorageSystemRestRep; import static com.emc.vipr.client.core.util.ResourceUtils.uri; import static com.emc.vipr.client.core.util.ResourceUtils.uris; import static controllers.Common.angularRenderArgs; import static controllers.Common.backToReferrer; import static controllers.Common.copyRenderArgsToAngular; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import play.Logger; import static util.BourneUtil.getViprClient; import java.net.URI; import java.net.URL; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import com.emc.storageos.model.NamedRelatedResourceRep; import com.emc.storageos.model.smis.StorageProviderRestRep; import com.emc.vipr.client.Task; import com.emc.vipr.client.ViPRCoreClient; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import controllers.Common; import controllers.deadbolt.Restrict; import controllers.deadbolt.Restrictions; import controllers.util.FlashException; import controllers.util.ViprResourceController; import models.StorageProviderTypes; import models.datatable.StorageProviderDataTable; import models.datatable.StorageProviderDataTable.StorageProviderInfo; import play.data.binding.As; import play.data.validation.MaxSize; import play.data.validation.MinSize; import play.data.validation.Required; import play.data.validation.Validation; import play.jobs.Job; import play.mvc.With; import util.DefaultStorageProviderPortMap; import util.EnumOption; import util.MessagesUtils; import util.StorageProviderUtils; import util.StorageSystemUtils; import util.StringOption; import util.validation.HostNameOrIpAddress; @With(Common.class) @Restrictions({ @Restrict("SYSTEM_ADMIN"), @Restrict("RESTRICTED_SYSTEM_ADMIN") }) public class StorageProviders extends ViprResourceController { protected static final String SAVED = "SMISProviders.saved"; protected static final String DELETED_SUCCESS = "SMISProviders.deleted.success"; protected static final String DELETED_ERROR = "SMISProviders.deleted.error"; protected static final String DELETE_NOT_ALLOWED = "SMISProviders.deleted.not.allowed"; protected static final String UNKNOWN = "SMISProviders.unknown"; protected static final String DISCOVERY_STARTED = "SMISProviders.introspection"; private static final int SAVE_WAIT_MILLIS = 300000; private static final String HTTPS = "https"; private static final String HYPERSCALEPORT = "8443"; //hardcode to be removed in next check-in private static final String UNITY = "unity"; private static final String VMAX = "vmax"; private static final String XTREMIO = "xtremio"; private static final String SUFFIX_ALL_FLASH = "F"; private static final String VIPR_START_GUIDE = "VIPR_START_GUIDE"; private static final String GUIDE_DATA = "GUIDE_DATA"; private static final String STORAGE_SYSTEMS = "storage_systems"; private static final String GUIDE_VISIBLE = "guideVisible"; private static final String GUIDE_COMPLETED_STEP = "completedSteps"; private static final String UNREGESTERD = "UNREGISTERED"; private static final String PROVIDER_TYPE = "StorageProvider"; private static void addReferenceData() { renderArgs.put("interfaceTypeOptions", StorageProviderTypes.getProviderOption()); renderArgs.put("optionsSIO", StorageProviderTypes.getScaleIoOption()); renderArgs.put("sslDefaultStorageProviderList", StorageProviderTypes.getProvidersWithSSL()); renderArgs.put("nonSSLStorageSystemList", StorageProviderTypes.getProvidersWithoutSSL()); renderArgs.put("mdmDefaultStorageProviderList", StorageProviderTypes.getProvidersWithMDM()); renderArgs.put("mdmonlyProviderList", StorageProviderTypes.getProvidersWithOnlyMDM()); renderArgs.put("secretKeyProviderList", StorageProviderTypes.getProvidersWithSecretKey()); renderArgs.put("elementManagerStorageProviderList", StorageProviderTypes.getProvidersWithEMS()); List<EnumOption> defaultStorageProviderPortMap = StorageProviderTypes.getStoragePortMap(); renderArgs.put("defaultStorageProviderPortMap", defaultStorageProviderPortMap); } private static void addAllFlashReferenceData() { renderArgs.put("interfaceTypeOptions", StorageProviderTypes.getAllFlashProviderOption()); renderArgs.put("optionsSIO", StorageProviderTypes.getScaleIoOption()); renderArgs.put("sslDefaultStorageProviderList", StorageProviderTypes.getProvidersWithSSL()); renderArgs.put("nonSSLStorageSystemList", StorageProviderTypes.getProvidersWithoutSSL()); renderArgs.put("mdmDefaultStorageProviderList", StorageProviderTypes.getProvidersWithMDM()); renderArgs.put("mdmonlyProviderList", StorageProviderTypes.getProvidersWithOnlyMDM()); renderArgs.put("secretKeyProviderList", StorageProviderTypes.getProvidersWithSecretKey()); renderArgs.put("elementManagerStorageProviderList", StorageProviderTypes.getProvidersWithEMS()); List<EnumOption> defaultStorageProviderPortMap = StorageProviderTypes.getStoragePortMap(); renderArgs.put("defaultStorageProviderPortMap", defaultStorageProviderPortMap); } public static void list() { renderArgs.put("dataTable", new StorageProviderDataTable()); render(); } public static void listJson() { performListJson(StorageProviderUtils.getStorageProviders(), new JsonItemOperation()); } public static void itemsJson(@As(",") String[] ids) { itemsJson(uris(ids)); } private static void itemsJson(List<URI> ids) { performItemsJson(StorageProviderUtils.getStorageProviders(ids), new JsonItemOperation()); } public static void discoveryCheckJson(@As(",") String[] ids) { List<String> failedDiscovery = new ArrayList<String>(); for (String id:ids) { StorageProviderRestRep storageProvider = StorageProviderUtils .getStorageProvider(uri(id)); if (storageProvider == null || storageProvider.getRegistrationStatus().equals(UNREGESTERD)) { //ignore for now continue; } if (!storageProvider.getScanStatus().equals("COMPLETE")){ failedDiscovery.add(storageProvider.getName()); continue; } Set<NamedRelatedResourceRep> storageSystems = StorageProviderUtils .getConnectedStorageSystems(uri(id)); boolean ssFound = false; for (NamedRelatedResourceRep storageSystem:storageSystems){ StorageSystemRestRep ss = StorageSystemUtils.getStorageSystem(storageSystem.getId()); if (ss != null) { if (!ss.getDiscoveryJobStatus().equals("COMPLETE")) { continue; } else { ssFound = true; } } } if (!ssFound){ failedDiscovery.add(storageProvider.getName()); continue; } } renderJSON(failedDiscovery); } public static void getAllFlashStorageSystemsList(@As(",") String[] ids) { List<Map<String,String>> storagesystemslist = new ArrayList<Map<String,String>>(); for (String id:ids) { if(id.contains(PROVIDER_TYPE)) { StorageProviderRestRep storageProvider = StorageProviderUtils.getStorageProvider(uri(id)); if (storageProvider == null) { continue; } Set<NamedRelatedResourceRep> storageSystems = StorageProviderUtils.getConnectedStorageSystems(uri(id)); for (NamedRelatedResourceRep storageSystem : storageSystems) { StorageSystemRestRep ss = StorageSystemUtils.getStorageSystem(storageSystem.getId()); if (ss != null && !ss.getRegistrationStatus().equals(UNREGESTERD)) { Map<String, String> ssMap = new HashMap<String, String>(); // Check if storage system is of type UNITY, VMAX or XtremIO if (StringUtils.equals(XTREMIO, ss.getSystemType())) { ssMap.put("id", ss.getId().toString()); ssMap.put("name", ss.getName()); storagesystemslist.add(ssMap); } if (StringUtils.equals(VMAX, ss.getSystemType())) { String modelType = ss.getModel(); if (modelType != null && modelType.contains(SUFFIX_ALL_FLASH)) { ssMap.put("id", ss.getId().toString()); ssMap.put("name", ss.getName()); storagesystemslist.add(ssMap); } } } } } else { StorageSystemRestRep ss = StorageSystemUtils.getStorageSystem(id); if (ss != null && !ss.getRegistrationStatus().equals(UNREGESTERD)) { Logger.info(ss.getId()+"-----"+ss.getSystemType()); Map<String, String> ssMap = new HashMap<String, String>(); // Check if storage system is of type UNITY, VMAX or XtremIO if (StringUtils.equals(XTREMIO, ss.getSystemType())) { ssMap.put("id", ss.getId().toString()); ssMap.put("name", ss.getName()); storagesystemslist.add(ssMap); } if (StringUtils.equals(VMAX, ss.getSystemType()) || StringUtils.equals(UNITY, ss.getSystemType())) { String modelType = ss.getModel(); if (modelType != null && modelType.contains(SUFFIX_ALL_FLASH)) { ssMap.put("id", ss.getId().toString()); ssMap.put("name", ss.getName()); storagesystemslist.add(ssMap); } } } } } renderJSON(storagesystemslist); } public static void itemDetails(String id) { StorageProviderRestRep storageProvider = StorageProviderUtils .getStorageProvider(uri(id)); if (storageProvider == null) { error(MessagesUtils.get(UNKNOWN, id)); } Set<NamedRelatedResourceRep> storageSystems = StorageProviderUtils .getConnectedStorageSystems(uri(id)); render(storageProvider, storageSystems); } public static void create() { // Check add is called from guide wizard, yes only AFA JsonObject jobject = getCookieAsJson(VIPR_START_GUIDE); String isGuideAdd = null; if (jobject != null && jobject.get(GUIDE_VISIBLE) != null) { isGuideAdd = jobject.get(GUIDE_VISIBLE).getAsString(); } if( isGuideAdd != null && StringUtils.equalsIgnoreCase(isGuideAdd, "true")) { addAllFlashReferenceData(); } else { addReferenceData(); } StorageProviderForm smisProvider = new StorageProviderForm(); // put all "initial create only" defaults here rather than field initializers smisProvider.interfaceType = StorageProviderTypes.SMIS; smisProvider.portNumber = getDefaultPort(DefaultStorageProviderPortMap.smis_useSSL); smisProvider.useSSL = true; copyRenderArgsToAngular(); angularRenderArgs().put("smisProvider", smisProvider); render("@edit", smisProvider); } private static Integer getDefaultPort(DefaultStorageProviderPortMap value) { String defaultValue = MessagesUtils .get(DefaultStorageProviderPortMap.class.getSimpleName() + "." + value.name()); return Integer.valueOf(StringUtils.defaultIfBlank(defaultValue, "0")); } @FlashException("list") public static void edit(String id) { addReferenceData(); StorageProviderRestRep provider = StorageProviderUtils.getStorageProvider(uri(id)); if (provider != null) { StorageProviderForm smisProvider = new StorageProviderForm(provider); copyRenderArgsToAngular(); angularRenderArgs().put("smisProvider", smisProvider); render(smisProvider); } else { flash.error(MessagesUtils.get(UNKNOWN, id)); list(); } } @FlashException(keep = true, referrer = { "create", "edit" }) public static void save(StorageProviderForm smisProvider) { smisProvider.validate("smisProvider"); if (Validation.hasErrors()) { Common.handleError(); } URI providerUri = smisProvider.save(); flash.success(MessagesUtils.get(SAVED, smisProvider.name)); //check if checklist is running on this step JsonObject jobject = getCookieAsJson(VIPR_START_GUIDE); if(jobject != null && jobject.get(GUIDE_COMPLETED_STEP) != null && jobject.get(GUIDE_VISIBLE) != null) { if (jobject.get(GUIDE_COMPLETED_STEP).getAsInt() == 3 && jobject.get(GUIDE_VISIBLE).getAsBoolean()) { JsonObject dataObject = getCookieAsJson(GUIDE_DATA); JsonArray storage_systems = dataObject.getAsJsonArray(STORAGE_SYSTEMS); if (storage_systems == null) { storage_systems = new JsonArray(); } boolean addToCookie = true; for(Object storageObject: storage_systems) { JsonObject storagearray = (JsonObject)storageObject; if(storagearray.get("id") != null) { String arrayId = storagearray.get("id").getAsString(); if(StringUtils.equals(arrayId, providerUri.toString())) { addToCookie = false; //update case, don't add in cookie break; } } } if (addToCookie) { JsonObject storage = new JsonObject(); storage.addProperty("id", providerUri.toString()); storage.addProperty("name", smisProvider.name); storage_systems.add(storage); dataObject.add(STORAGE_SYSTEMS, storage_systems); saveJsonAsCookie(GUIDE_DATA, dataObject); } list(); } } backToReferrer(); list(); } @FlashException("list") public static void delete(@As(",") String[] ids) { delete(uris(ids)); } private static void delete(List<URI> ids) { if (StorageProviderUtils.hasStorageSystems(ids)) { flash.error(MessagesUtils.get(DELETE_NOT_ALLOWED)); list(); } List<OperationResult<Void, URI>> results = perform(ids, new DeactivateOperation()); List<OperationResult<Void, URI>> failed = getFailedResults(results); if (failed.isEmpty()) { flash.success(MessagesUtils.get(DELETED_SUCCESS)); } else { String errorMessage = StringUtils.join(errorMessages(failed), "\n"); int total = results.size(); int deleted = total - failed.size(); flash.error(MessagesUtils.get(DELETED_ERROR, deleted, total, errorMessage)); } list(); } @FlashException("list") public static void discover() { // API only does not support discovery of a single SMI-S provider StorageProviderUtils.discoverAll(); flash.success(MessagesUtils.get(DISCOVERY_STARTED)); list(); } // Suppressing Sonar violation of Password Hardcoded. Password is not hardcoded here // Suppressing sonar violation for need of accessor methods. Accessor methods are not needed and we use public variables @SuppressWarnings("squid:S2068 , ClassVariableVisibilityCheck") public static class StorageProviderForm { public String id; @MaxSize(128) @MinSize(2) @Required public String name; @MaxSize(2048) public String userName; @HostNameOrIpAddress @Required public String ipAddress; @Required public Integer portNumber; @MaxSize(2048) public String password = ""; @MaxSize(2048) public String confirmPassword = ""; public Boolean useSSL; public String interfaceType; @MaxSize(2048) public String secondaryUsername; @MaxSize(2048) public String secondaryPassword = ""; @MaxSize(2048) public String secondaryPasswordConfirm = ""; public String elementManagerURL; public String secondaryURL; public String secretKey; @MaxSize(2048) public String hyperScaleUser; @MaxSize(2048) public String hyperScalePassword = ""; @MaxSize(2048) public String hyperScaleConfPasswd = ""; public String hyperScaleHost; public String hyperScalePort; public URL url; public StorageProviderForm() { } public StorageProviderForm(StorageProviderRestRep smisProvider) { readFrom(smisProvider); } public boolean isNew() { return StringUtils.isBlank(id); } private boolean isXIV() { return StorageProviderTypes.isXIV(interfaceType); } public boolean isScaleIOApi() { return StorageProviderTypes.isScaleIOApi(interfaceType); } public boolean isCeph() { return StorageProviderTypes.isCeph(interfaceType); } public void setXIVParameters() { if (StringUtils.isNotEmpty(this.hyperScaleUser)) { this.secondaryUsername = this.hyperScaleUser; } if (StringUtils.isNotEmpty(this.hyperScalePassword)) { this.secondaryPassword = this.hyperScalePassword; } if (StringUtils.isNotEmpty(this.hyperScaleConfPasswd)) { this.secondaryPasswordConfirm = this.hyperScaleConfPasswd; } if (StringUtils.isNotEmpty(this.hyperScaleHost) && StringUtils.isNotEmpty(this.hyperScalePort)) { try { url = new URL(HTTPS, this.hyperScaleHost, Integer.parseInt(this.hyperScalePort), ""); } catch (Exception e) { flash.error("Unable to parse Hyper Scale Manager URL"); } this.secondaryURL = url.toString(); } else if ((StringUtils.isNotEmpty(this.hyperScaleHost) || StringUtils.isNotEmpty(this.hyperScalePort))){ flash.error("Secondary Host or Port is Missing"); edit(id); } } public void readFrom(StorageProviderRestRep storageProvider) { this.id = storageProvider.getId().toString(); this.name = storageProvider.getName(); this.ipAddress = storageProvider.getIPAddress(); this.userName = storageProvider.getUserName(); this.password = ""; // the platform will never return the real // password this.portNumber = storageProvider.getPortNumber(); this.useSSL = storageProvider.getUseSSL(); this.interfaceType = storageProvider.getInterface(); this.secondaryUsername = storageProvider.getSecondaryUsername(); this.secondaryPassword = ""; // the platform will never return the real password this.secondaryURL = storageProvider.getSecondaryURL(); this.elementManagerURL = storageProvider.getElementManagerURL(); this.secretKey = ""; // the platform will never return the real key this.hyperScaleUser = storageProvider.getSecondaryUsername(); this.hyperScalePassword = ""; // the platform will never return the real password if(!StringUtils.isEmpty(secondaryURL)) { try { url = new URL(this.secondaryURL); } catch(Exception e) { flash.error("Unable to parse Hyper Scale Manager URL"); } if(null!=url) { this.hyperScaleHost = url.getHost(); this.hyperScalePort = Integer.toString(url.getPort()); } } if (isScaleIOApi()) { this.secondaryUsername = this.userName; this.secondaryPassword = this.password; this.secondaryPasswordConfirm = this.confirmPassword; } setXIVParameters(); } public URI save() { setXIVParameters(); if (isNew()) { return create().getResourceId(); } else { return update().getId(); } } public StorageProviderRestRep update() { return StorageProviderUtils.update(uri(id), name, ipAddress, portNumber, userName, password, useSSL, interfaceType, secondaryUsername, secondaryPassword, elementManagerURL, secondaryURL, secretKey); } public Task<StorageProviderRestRep> create() { Task<StorageProviderRestRep> task = StorageProviderUtils.create( name, ipAddress, portNumber, userName, password, useSSL, interfaceType, secondaryUsername, secondaryPassword, elementManagerURL, secondaryURL, secretKey); new SaveWaitJob(getViprClient(), task).now(); return task; } public void validate(String fieldName) { Validation.valid(fieldName, this); if (isScaleIOApi() ) { Validation.required(fieldName + ".secondaryPassword", this.secondaryPassword); Validation.required(fieldName + ".secondaryPasswordConfirm", this.secondaryPasswordConfirm); } else if (isCeph()) { Validation.required(fieldName + ".userName", this.userName); Validation.required(fieldName + ".secretKey", this.secretKey); } else if (isNew()) { Validation.required(fieldName + ".userName", this.userName); Validation.required(fieldName + ".password", this.password); Validation.required(fieldName + ".password", this.password); Validation.required(fieldName + ".confirmPassword", this.confirmPassword); } else if (isXIV()) { if ((StringUtils.isNotEmpty(this.hyperScaleHost) || StringUtils.isNotEmpty(this.hyperScalePort))) { Validation.addError(fieldName + ".hyperScaleHost","Either Secondary Host or Port details is missing"); Validation.addError(fieldName + ".hyperScalePort","Either Secondary Host or Port details is missing"); } } if (!StringUtils.equals(StringUtils.trim(password), StringUtils.trim(confirmPassword))) { Validation.addError(fieldName + ".confirmPassword", MessagesUtils .get("smisProvider.confirmPassword.not.match")); } if (!StringUtils.equals(StringUtils.trim(secondaryPassword), StringUtils.trim(secondaryPasswordConfirm))) { Validation .addError( fieldName + ".secondaryPasswordConfirm", MessagesUtils .get("smisProvider.secondaryPassword.confirmPassword.not.match")); } } } @SuppressWarnings("rawtypes") private static class SaveWaitJob extends Job { private final ViPRCoreClient client; private final Task<StorageProviderRestRep> task; public SaveWaitJob(ViPRCoreClient client, Task<StorageProviderRestRep> task) { this.client = client; this.task = task; } @Override public void doJob() throws Exception { try { task.waitFor(SAVE_WAIT_MILLIS); } catch (Exception e) { // Ignore, trying to ensure basic SMI-S discovery completes // before kicking off storage system discovery } client.storageSystems().discoverAll(); } } protected static class JsonItemOperation implements ResourceValueOperation<StorageProviderInfo, StorageProviderRestRep> { @Override public StorageProviderInfo performOperation( StorageProviderRestRep provider) throws Exception { return new StorageProviderInfo(provider); } } protected static class DeactivateOperation implements ResourceIdOperation<Void> { @Override public Void performOperation(URI id) throws Exception { StorageProviderUtils.deactivate(id); return null; } } }