/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package controllers.arrays;
import static com.emc.vipr.client.core.util.ResourceUtils.id;
import static com.emc.vipr.client.core.util.ResourceUtils.uris;
import static controllers.Common.flashException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jobs.vipr.ConnectedFileVirtualPoolsCall;
import jobs.vipr.TenantsCall;
import jobs.vipr.VirtualArraysCall;
import models.FileProtectionSystemTypes;
import models.FileProtocols;
import models.FileReplicationCopyMode;
import models.FileRpoType;
import models.PoolAssignmentTypes;
import models.ProvisioningTypes;
import models.RemoteCopyMode;
import models.StorageSystemTypes;
import models.datatable.StoragePoolDataTable;
import models.datatable.StoragePoolDataTable.StoragePoolInfo;
import models.datatable.VirtualPoolDataTable;
import models.datatable.VirtualPoolDataTable.VirtualPoolInfo;
import models.virtualpool.FileVirtualPoolForm;
import models.virtualpool.ReplicationCopyForm;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.data.binding.As;
import play.data.validation.Validation;
import play.exceptions.UnexpectedException;
import play.i18n.Messages;
import play.libs.F.Promise;
import play.mvc.Catch;
import play.mvc.Http;
import play.mvc.With;
import util.MessagesUtils;
import util.StorageSystemUtils;
import util.StringOption;
import util.TenantUtils;
import util.ValidationResponse;
import util.VirtualArrayUtils;
import util.VirtualPoolUtils;
import util.datatable.DataTablesSupport;
import com.emc.storageos.model.pools.StoragePoolRestRep;
import com.emc.storageos.model.varray.VirtualArrayRestRep;
import com.emc.storageos.model.vpool.FileVirtualPoolRestRep;
import com.emc.vipr.client.exceptions.ViPRException;
import com.emc.vipr.client.exceptions.ViPRHttpException;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import controllers.Common;
import controllers.deadbolt.Restrict;
import controllers.deadbolt.Restrictions;
import controllers.util.FlashException;
import controllers.util.ViprResourceController;
@With(Common.class)
@Restrictions({ @Restrict("SYSTEM_ADMIN"), @Restrict("RESTRICTED_SYSTEM_ADMIN") })
public class FileVirtualPools extends ViprResourceController {
protected static final String SAVED_SUCCESS = "VirtualPools.save.success";
protected static final String SAVED_ERROR = "VirtualPools.save.error";
protected static final String DELETED_SUCCESS = "VirtualPools.delete.success";
protected static final String DELETED_ERROR = "VirtualPools.delete.error";
protected static final String UNKNOWN = "VirtualPools.unknown";
private static void backToReferrer() {
String referrer = Common.getReferrer();
if (StringUtils.isNotBlank(referrer)) {
redirect(referrer);
}
else {
list();
}
}
private static void throwFlashException(FileVirtualPoolForm vpool, ViPRException e) {
flashException(e);
setErrorReferrer(vpool);
}
private static void setErrorReferrer(FileVirtualPoolForm vpool) {
// Only flash vpool parameters to reduce amount stored in flash scope
for (String param : params.all().keySet()) {
if (param.startsWith("vpool.")) {
params.flash(param);
}
}
Validation.keep();
if (vpool.isNew()) {
create();
}
else {
edit(vpool.id);
}
}
public static void list() {
VirtualPoolDataTable dataTable = createVirtualPoolDataTable();
render(dataTable);
}
private static VirtualPoolDataTable createVirtualPoolDataTable() {
VirtualPoolDataTable dataTable = new VirtualPoolDataTable();
dataTable.alterColumn("provisionedAs").hidden();
dataTable.alterColumn("provisioningType").setVisible(true);
return dataTable;
}
public static void listJson() {
List<VirtualPoolInfo> items = Lists.newArrayList();
for (FileVirtualPoolRestRep virtualPool : VirtualPoolUtils.getFileVirtualPools()) {
items.add(new VirtualPoolInfo(virtualPool));
}
renderJSON(DataTablesSupport.createJSON(items, params));
}
public static void create() {
FileVirtualPoolForm form = new FileVirtualPoolForm();
// Set default values
form.protocols = Sets.newHashSet(FileProtocols.CIFS, FileProtocols.NFS);
edit(form);
}
public static void duplicate(String ids) {
FileVirtualPoolRestRep targetVpool = VirtualPoolUtils.getFileVirtualPool(ids);
if (targetVpool == null) {
flash.error(MessagesUtils.get(UNKNOWN, ids));
backToReferrer();
}
FileVirtualPoolForm copy = new FileVirtualPoolForm();
copy.load(targetVpool);
copy.id = null;
copy.name = Messages.get("virtualPools.duplicate.name", copy.name);
// Target VPool could have resources, set resources to 0 on the new Copy VPool so user can modify form
copy.numResources = 0;
edit(copy);
}
public static void edit(String id) {
FileVirtualPoolRestRep virtualPool = VirtualPoolUtils.getFileVirtualPool(id);
if (virtualPool == null) {
flash.error(MessagesUtils.get(UNKNOWN, id));
backToReferrer();
}
FileVirtualPoolForm form = new FileVirtualPoolForm();
form.load(virtualPool);
edit(form);
}
private static void edit(FileVirtualPoolForm vpool) {
addStaticOptions();
addDynamicOptions(vpool);
renderArgs.put("storagePoolsDataTable", createStoragePoolDataTable());
render("@edit", vpool);
}
private static StoragePoolDataTable createStoragePoolDataTable() {
StoragePoolDataTable dataTable = new StoragePoolDataTable();
dataTable.alterColumn("registrationStatus").hidden();
dataTable.alterColumn("driveTypes").hidden();
return dataTable;
}
@FlashException(keep = true, referrer = { "create", "edit" })
public static void save(FileVirtualPoolForm vpool) {
if (vpool == null) {
list();
}
vpool.deserialize();
vpool.validate("vpool");
if (Validation.hasErrors()) {
Common.handleError();
}
try {
FileVirtualPoolRestRep result = vpool.save();
flash.success(MessagesUtils.get(SAVED_SUCCESS, result.getName()));
backToReferrer();
} catch (ViPRException e) {
throwFlashException(vpool, e);
}
}
/**
* Handles errors that might arise during JSON requests and returns the error message.
*
* @param e
*/
@Catch({ UnexpectedException.class, ViPRException.class })
public static void handleJsonError(Exception e) {
if (request.isAjax() || StringUtils.endsWithIgnoreCase(request.action, "json")) {
Throwable cause = Common.unwrap(e);
String message = Common.getUserMessage(cause);
Logger.error(e, "AJAX request failed: %s.%s [%s]", request.controller, request.action, message);
error(message);
}
}
public static void listStoragePoolsJson(FileVirtualPoolForm vpool) {
List<StoragePoolInfo> items = Lists.newArrayList();
if (vpool != null && vpool.protocols != null && !vpool.protocols.isEmpty()) {
vpool.deserialize();
Map<URI, String> storageSystemNames = StorageSystemUtils.getStorageSystemNames();
List<StoragePoolRestRep> pools = getMatchingStoragePools(vpool);
for (StoragePoolRestRep pool : pools) {
String storageSystemName = storageSystemNames.get(id(pool.getStorageSystem()));
items.add(new StoragePoolInfo(pool, storageSystemName));
}
}
renderJSON(DataTablesSupport.createJSON(items, params));
}
private static List<StoragePoolRestRep> getMatchingStoragePools(FileVirtualPoolForm vpool) {
try {
return await(vpool.matchingStoragePools().asPromise());
} catch (UnexpectedException e) {
Throwable cause = Common.unwrap(e);
if (cause instanceof ViPRHttpException) {
int httpCode = ((ViPRHttpException) cause).getHttpCode();
// Bad request is usually a result of partially completing the form
if (httpCode == Http.StatusCode.BAD_REQUEST) {
Logger.warn(cause, "Bad Request when querying matching storage pools, returning no matches");
return Lists.newArrayList();
}
}
throw e;
}
}
public static void listVirtualArrayAttributesJson(FileVirtualPoolForm vpool) {
if (vpool == null) {
renderJSON(Collections.emptyList());
}
renderJSON(vpool.getVirtualPoolAttributes());
}
public static void delete(@As(",") String[] ids) {
delete(uris(ids));
}
private static void delete(List<URI> ids) {
performSuccessFail(ids, new DeactivateOperation(), DELETED_SUCCESS, DELETED_ERROR);
backToReferrer();
}
private static void addStaticOptions() {
renderArgs.put("provisioningTypeOptions",
ProvisioningTypes.options(ProvisioningTypes.THICK, ProvisioningTypes.THIN));
renderArgs.put("protocolOptions", FileProtocols.options(FileProtocols.CIFS, FileProtocols.NFS, FileProtocols.NFSV4));
renderArgs.put("systemTypeOptions", StorageSystemTypes.getFileStorageOptions());
renderArgs.put("poolAssignmentOptions",
PoolAssignmentTypes.options(PoolAssignmentTypes.AUTOMATIC, PoolAssignmentTypes.MANUAL));
renderArgs.put("varrayAttributeNames", VirtualArrayUtils.ATTRIBUTES);
renderArgs.put("replicationTypeOptions", FileProtectionSystemTypes.PROTECTION_SYSTEM_OPTIONS);
renderArgs.put("replicationModeOptions", FileReplicationCopyMode.OPTIONS);
renderArgs.put("replicationRpoTypeOptions", FileRpoType.RPO_OPTIONS);
}
private static void addDynamicOptions(FileVirtualPoolForm vpool) {
// Runs all queries in jobs
Promise<List<VirtualArrayRestRep>> virtualArrays = new VirtualArraysCall().asPromise();
if (TenantUtils.canReadAllTenants() && VirtualPoolUtils.canUpdateACLs()) {
addDataObjectOptions("tenantOptions", new TenantsCall().asPromise());
}
addDataObjectOptions("virtualArrayOptions", virtualArrays);
}
protected static class DeactivateOperation implements ResourceIdOperation<Void> {
@Override
public Void performOperation(URI id) throws Exception {
VirtualPoolUtils.deactivateFile(id);
return null;
}
}
public static void listReplicationVirtualArraysJson(FileVirtualPoolForm vpool) {
if (vpool == null) {
renderJSON(Collections.emptyList());
}
vpool.deserialize();
List<StringOption> actualOptions = Lists.newArrayList();
List<VirtualArrayRestRep> virtualArrays = VirtualArrayUtils.getVirtualArrays();
for (StringOption option : dataObjectOptions(virtualArrays)) {
actualOptions.add(option);
}
renderJSON(actualOptions);
}
public static void listReplicationVirtualPoolsJson(String virtualArray) {
if (virtualArray == null) {
renderJSON(Collections.emptyList());
}
List<FileVirtualPoolRestRep> pools = await(new ConnectedFileVirtualPoolsCall(uris(virtualArray)).asPromise());
renderJSON(dataObjectOptions(pools));
}
public static void validateReplicationCopy(ReplicationCopyForm replicationCopy) {
if (replicationCopy == null) {
renderJSON(ValidationResponse.invalid());
}
replicationCopy.validate("replicationCopy");
if (Validation.hasErrors()) {
renderJSON(ValidationResponse.collectErrors());
}
else {
renderJSON(ValidationResponse.valid());
}
}
}