/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package controllers.resources; import static com.emc.vipr.client.core.util.ResourceUtils.uri; import static com.emc.vipr.client.core.util.ResourceUtils.uris; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import models.datatable.FileSnapshotsDataTable; import models.datatable.ShareACLDataTable; import org.apache.commons.lang.StringUtils; import play.data.binding.As; import play.data.validation.Required; import play.data.validation.Validation; import play.mvc.With; import util.BourneUtil; import util.FileUtils; import util.FileUtils.ExportRuleInfo; import util.MessagesUtils; import util.datatable.DataTablesSupport; import com.emc.storageos.model.file.ExportRule; import com.emc.storageos.model.file.ExportRules; import com.emc.storageos.model.file.FileShareRestRep; import com.emc.storageos.model.file.FileSnapshotRestRep; import com.emc.storageos.model.file.FileSystemShareParam; import com.emc.storageos.model.file.ShareACL; import com.emc.storageos.model.file.ShareACLs; import com.emc.storageos.model.file.SmbShareResponse; import com.emc.storageos.model.file.SnapshotCifsShareACLUpdateParams; import com.emc.storageos.model.file.SnapshotExportUpdateParams; import com.emc.storageos.volumecontroller.FileShareExport; import com.emc.vipr.client.Task; import com.emc.vipr.client.Tasks; import com.emc.vipr.client.ViPRCoreClient; import com.emc.vipr.client.exceptions.ServiceErrorException; import com.emc.vipr.client.exceptions.ViPRHttpException; import com.google.common.collect.Lists; import controllers.Common; import controllers.util.FlashException; @With(Common.class) public class FileSnapshots extends ResourceController { private static final String UNKNOWN = "resources.filesnapshots.unknown"; private static FileSnapshotsDataTable fileSnapshotsDataTable = new FileSnapshotsDataTable(); public static void snapshots(String projectId) { setActiveProjectId(projectId); renderArgs.put("dataTable", fileSnapshotsDataTable); addReferenceData(); render(); } public static void snapshotsJson(String projectId) { if (StringUtils.isNotBlank(projectId)) { setActiveProjectId(projectId); } else { projectId = getActiveProjectId(); } List<FileSnapshotsDataTable.FileSnapshot> fileSnapshots = FileSnapshotsDataTable.fetch(uri(projectId)); renderJSON(DataTablesSupport.createJSON(fileSnapshots, params)); } public static void snapshot(String snapshotId) { ViPRCoreClient client = BourneUtil.getViprClient(); FileSnapshotRestRep snapshot = null; try { snapshot = client.fileSnapshots().get(uri(snapshotId)); } catch (ViPRHttpException e) { if (e.getHttpCode() == 404 || e.getHttpCode() == 400) { flash.error(MessagesUtils.get(UNKNOWN, snapshotId)); snapshots(null); } throw e; } if (snapshot != null) { if (snapshot.getParent() != null) { FileShareRestRep fileSystem = client.fileSystems().get(snapshot.getParent()); renderArgs.put("fileSystem", fileSystem); } Tasks<FileSnapshotRestRep> tasksResponse = client.fileSnapshots().getTasks(snapshot.getId()); List<Task<FileSnapshotRestRep>> tasks = tasksResponse.getTasks(); renderArgs.put("tasks", tasks); } else { flash.error(MessagesUtils.get(UNKNOWN, snapshotId)); snapshots(null); } render(snapshot); } public static void snapshotExports(String snapshotId) { ViPRCoreClient client = BourneUtil.getViprClient(); List<ExportRule> exports = client.fileSnapshots().getExport(uri(snapshotId), true, ""); renderArgs.put("permissionTypeOptions", Lists.newArrayList(FileShareExport.Permissions.values())); render(exports); } public static void fileSnapshotExportsJson(String id, String path, String sec) { ExportRuleInfo infos = FileUtils.getFSExportRulesInfo(uri(id), path, sec); renderJSON(infos); } public static void snapshotShares(String snapshotId) { ViPRCoreClient client = BourneUtil.getViprClient(); List<SmbShareResponse> shares = client.fileSnapshots().getShares(uri(snapshotId)); render(shares); } @FlashException(referrer = { "snapshot" }) public static void deleteSnapshotShare(String snapshotId, String shareName) { ViPRCoreClient client = BourneUtil.getViprClient(); Task<FileSnapshotRestRep> task = client.fileSnapshots().removeShare(uri(snapshotId), shareName); flash.put("info", MessagesUtils.get("resources.filesnapshot.share.deactivate")); snapshot(snapshotId); } @FlashException(referrer = { "snapshot" }) public static void deleteSnapshotExport(String snapshotId, String security, String exportPath) { ViPRCoreClient client = BourneUtil.getViprClient(); ExportRule rule = new ExportRule(); rule.setSecFlavor(security); List<ExportRule> list = Lists.newArrayList(); list.add(rule); ExportRules exportRules = new ExportRules(); exportRules.setExportRules(list); SnapshotExportUpdateParams params = new SnapshotExportUpdateParams(); params.setExportRulesToDelete(exportRules); FileSnapshotRestRep snapshot = client.fileSnapshots().get(uri(snapshotId)); String subDir = FileUtils.findSubDirectory(snapshot.getMountPath(), exportPath); client.fileSnapshots().updateExport(uri(snapshotId), subDir, params); flash.put("info", MessagesUtils.get("resources.filesnapshot.export.deactivate")); snapshot(snapshotId); } @FlashException(referrer = { "snapshot" }) public static void deleteFileSnapshot(String fileSnapshotId) { if (StringUtils.isNotBlank(fileSnapshotId)) { ViPRCoreClient client = BourneUtil.getViprClient(); Task<FileSnapshotRestRep> task = client.fileSnapshots().deactivate(uri(fileSnapshotId)); flash.put("info", MessagesUtils.get("resources.filesnapshot.deactivate")); } snapshot(fileSnapshotId); } @FlashException(value = "snapshots") public static void delete(@As(",") String[] ids) { delete(uris(ids)); } private static void delete(List<URI> ids) { if (ids != null) { ViPRCoreClient client = BourneUtil.getViprClient(); List<Task<FileSnapshotRestRep>> tasks = Lists.newArrayList(); for (URI id : ids) { Task<FileSnapshotRestRep> task = client.fileSnapshots().deactivate(id); tasks.add(task); } if (!tasks.isEmpty()) { flash.put("info", MessagesUtils.get("resources.filesnapshots.deactivate", tasks.size())); } } snapshots(null); } @FlashException(referrer = { "snapshot" }) public static void save(Boolean edit, String id, String fsPath, String exportPath, String security, String anon, @As(",") List<String> ro, @As(",") List<String> rw, @As(",") List<String> root) { ExportRule rule = new ExportRule(); rule.setAnon(anon); rule.setSecFlavor(security); // Clean up endpoints list by removing all empty items if any List<String> empty = Arrays.asList(""); rw.removeAll(empty); ro.removeAll(empty); root.removeAll(empty); if (!ro.isEmpty()) { rule.setReadOnlyHosts(FileUtils.buildEndpointList(ro)); } if (!rw.isEmpty()) { rule.setReadWriteHosts(FileUtils.buildEndpointList(rw)); } if (!root.isEmpty()) { rule.setRootHosts(FileUtils.buildEndpointList(root)); } List<ExportRule> addRules = Lists.newArrayList(); addRules.add(rule); ExportRules exportRules = new ExportRules(); exportRules.setExportRules(addRules); SnapshotExportUpdateParams params = new SnapshotExportUpdateParams(); if (!edit) { params.setExportRulesToAdd(exportRules); } else { params.setExportRulesToModify(exportRules); } ViPRCoreClient client = BourneUtil.getViprClient(); client.fileSnapshots().updateExport(uri(id), null, params); flash.put("info", MessagesUtils.get("resources.filesystem.export.update")); snapshot(id); } /** * This method called when Access Control List page is loaded. * * @param snapShotId * SnapShot id of the provided SnapShot. * @param shareName * Name of the file share. */ public static void listSnapshotAcl(String snapshotId, String shareName) { renderArgs.put("dataTable", new ShareACLDataTable()); renderArgs.put("snapshotId", uri(snapshotId)); renderArgs.put("shareName", shareName); renderArgs.put("aclURL", "/file/snapshots/" + uri(snapshotId) + "/shares/" + shareName + "/acl"); ViPRCoreClient client = BourneUtil.getViprClient(); FileSnapshotRestRep restRep = client.fileSnapshots().get(uri(snapshotId)); renderArgs.put("snapshotName", restRep.getName()); SnapshotShareACLForm shareACL = new SnapshotShareACLForm(); render(shareACL); } /** * This method called during population of Datatable for ACL data. * * @param aclURL * URL of the file system share. */ public static void listSnapshotAclJson(String aclURL) { String snapshotId = null; String shareName = null; if (StringUtils.isNotBlank(aclURL)) { String[] parts = aclURL.split("/"); if (parts.length == 7) { snapshotId = parts[3]; shareName = parts[5]; } } ViPRCoreClient client = BourneUtil.getViprClient(); List<ShareACL> shareAcls = client.fileSnapshots().getShareACLs(uri(snapshotId), shareName); List<ShareACLDataTable.SnapshotAclInfo> acl = Lists.newArrayList(); for (ShareACL shareAcl : shareAcls) { String userOrGroup = shareAcl.getUser(); String type = SnapshotShareACLForm.USER; if (shareAcl.getGroup() != null && shareAcl.getGroup() != "") { type = SnapshotShareACLForm.GROUP; userOrGroup = shareAcl.getGroup(); } acl.add(new ShareACLDataTable.SnapshotAclInfo(userOrGroup, type, shareAcl.getPermission(), snapshotId, shareName, shareAcl .getDomain())); } renderJSON(DataTablesSupport.createJSON(acl, params)); } /** * This method called When user selects ACLs and hit delete button. * * @param aclURL * URL of the snapshot share. * @param ids * ids of the selected ACL */ @FlashException(referrer = { "snapshot" }) public static void removeSnapShotAcl(String aclUrl, @As(",") String[] ids) { ShareACLs aclsToDelete = new ShareACLs(); List<ShareACL> shareAcls = new ArrayList<ShareACL>(); String snapshotId = null; String shareName = null; if (ids != null && ids.length > 0) { for (String id : ids) { String type = SnapshotShareACLForm.extractTypeFromId(id); String name = SnapshotShareACLForm.extractNameFromId(id); String domain = SnapshotShareACLForm.extractDomainFromId(id); snapshotId = SnapshotShareACLForm.extractSnapshotFromId(id); shareName = SnapshotShareACLForm.extractShareNameFromId(id); ShareACL ace = new ShareACL(); if (SnapshotShareACLForm.GROUP.equalsIgnoreCase(type)) { ace.setGroup(name); } else { ace.setUser(name); } if (domain != null && !"".equals(domain) && !"null".equals(domain)) { ace.setDomain(domain); } shareAcls.add(ace); } aclsToDelete.setShareACLs(shareAcls); SnapshotCifsShareACLUpdateParams input = new SnapshotCifsShareACLUpdateParams(); input.setAclsToDelete(aclsToDelete); ViPRCoreClient client = BourneUtil.getViprClient(); client.fileSnapshots().updateShareACL(uri(snapshotId), shareName, input); } flash.success(MessagesUtils.get("resources.filesystem.share.acl.deleted")); listSnapshotAcl(snapshotId, shareName); } /** * This method called when user Adds an access control entry from UI. * * @param aclURL * URL of the snapshot share. * @param shareACL * This contains the form data from UI. */ public static void addSnapshotAcl(String aclURL, SnapshotShareACLForm shareACL, String formAccessControlList) { String snapshotId = null; String shareName = null; if (StringUtils.isNotBlank(aclURL)) { String[] parts = aclURL.split("/"); if (parts.length == 7) { snapshotId = parts[3]; shareName = parts[5]; } } if (formAccessControlList == null || "".equals(formAccessControlList)) { flash.error(MessagesUtils.get("resources.filesystem.share.acl.invalid.settings"), null); listSnapshotAcl(snapshotId, shareName); } SnapshotCifsShareACLUpdateParams input = createSnapshotCifsShareAclParams(formAccessControlList); ViPRCoreClient client = BourneUtil.getViprClient(); try { client.fileSnapshots().updateShareACL(uri(snapshotId), shareName, input); } catch (ServiceErrorException e) { flash.error(e.getMessage(), null); listSnapshotAcl(snapshotId, shareName); } flash.success(MessagesUtils.get("resources.filesystem.share.acl.added")); listSnapshotAcl(snapshotId, shareName); } private static SnapshotCifsShareACLUpdateParams createSnapshotCifsShareAclParams(String formData) { String[] uiAcls = formData.split(","); List<ShareACL> acls = new ArrayList<ShareACL>(); for (String uiAce : uiAcls) { String[] uiData = uiAce.split("~~~"); String uiType = uiData[0]; String uiName = uiData[1]; String uiDomain = uiData[2]; String uiPermission = uiData[3]; ShareACL shareAcl = new ShareACL(); if ("GROUP".equalsIgnoreCase(uiType)) { shareAcl.setGroup(uiName); } else { shareAcl.setUser(uiName); } shareAcl.setPermission(uiPermission); if (uiDomain != null && !"".equals(uiDomain)) { shareAcl.setDomain(uiDomain); } acls.add(shareAcl); } SnapshotCifsShareACLUpdateParams input = new SnapshotCifsShareACLUpdateParams(); ShareACLs aclsToAdd = new ShareACLs(); aclsToAdd.setShareACLs(acls); input.setAclsToAdd(aclsToAdd); return input; } /** * This method called when user adds sub directory. * * @param id * id of the file system. * @param shareName * shareName of the sub directory. * @param subDirectory * name of the sub directory. * @param description * Given description during creation of sub directory. */ @FlashException(referrer = { "snapshot" }) public static void addSnapShotSubDir(String id, String shareName, String subDirectroy, String description) { ViPRCoreClient client = BourneUtil.getViprClient(); FileSystemShareParam param = new FileSystemShareParam(); param.setShareName(shareName); if (subDirectroy != null && !"".equals(subDirectroy)) { param.setSubDirectory(subDirectroy); } if (description != null && !"".equals(description)) { param.setDescription(description); } client.fileSnapshots().share(uri(id), param); flash.put("info", MessagesUtils.get("resources.filesystem.subdir.add")); snapshot(id); } /** * This static class holds the form data for the ACL page. */ public static class SnapshotShareACLForm { private static final String ID_DELIMITER = "~~~~"; public static final String USER = "User"; public static final String GROUP = "Group"; public String domain; public String id; @Required public String name; @Required public String type = USER; @Required public String permission = "Read"; public SnapshotCifsShareACLUpdateParams createSnapshotCifsShareAclParams() { ShareACL shareAcl = new ShareACL(); if (GROUP.equalsIgnoreCase(type.trim())) { shareAcl.setGroup(name.trim()); } else { shareAcl.setUser(name.trim()); } shareAcl.setPermission(permission); if (domain.trim() != null && !"".equals(domain.trim())) { shareAcl.setDomain(domain.trim()); } List<ShareACL> acls = new ArrayList<ShareACL>(); acls.add(shareAcl); SnapshotCifsShareACLUpdateParams input = new SnapshotCifsShareACLUpdateParams(); ShareACLs aclsToAdd = new ShareACLs(); aclsToAdd.setShareACLs(acls); input.setAclsToAdd(aclsToAdd); return input; } public void validate(String formName) { Validation.valid(formName, this); Validation.required(formName + ".name", name); Validation.required(formName + ".type", type); Validation.required(formName + ".permission", permission); if (name == null || "".equals(name)) { Validation.addError(formName + ".name", "resources.filesystem.share.acl.invalid.name"); } } public static String createId(String name, String type, String fileSystem, String shareName, String domain) { return name + ID_DELIMITER + type + ID_DELIMITER + fileSystem + ID_DELIMITER + shareName + ID_DELIMITER + domain; } public static String extractNameFromId(String id) { if (StringUtils.isNotBlank(id)) { String[] parts = id.split(ID_DELIMITER); if (parts.length == 5) { return parts[0]; } else { return id; } } return null; } public static String extractTypeFromId(String id) { if (StringUtils.isNotBlank(id)) { String[] parts = id.split(ID_DELIMITER); if (parts.length == 5) { return parts[1]; } else { return id; } } return null; } public static String extractSnapshotFromId(String id) { if (StringUtils.isNotBlank(id)) { String[] parts = id.split(ID_DELIMITER); if (parts.length == 5) { return parts[2]; } else { return id; } } return null; } public static String extractShareNameFromId(String id) { if (StringUtils.isNotBlank(id)) { String[] parts = id.split(ID_DELIMITER); if (parts.length == 5) { return parts[3]; } else { return id; } } return null; } public static String extractDomainFromId(String id) { if (StringUtils.isNotBlank(id)) { String[] parts = id.split(ID_DELIMITER); if (parts.length == 5) { return parts[4]; } else { return id; } } return null; } } }