/*
* Copyright (c) 2008-2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.datadomain;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.datadomain.restapi.DataDomainApiConstants;
import com.emc.storageos.datadomain.restapi.DataDomainClient;
import com.emc.storageos.datadomain.restapi.DataDomainClientFactory;
import com.emc.storageos.datadomain.restapi.errorhandling.DataDomainApiException;
import com.emc.storageos.datadomain.restapi.errorhandling.DataDomainResourceNotFoundException;
import com.emc.storageos.datadomain.restapi.model.DDExportClient;
import com.emc.storageos.datadomain.restapi.model.DDExportClientModify;
import com.emc.storageos.datadomain.restapi.model.DDExportInfo;
import com.emc.storageos.datadomain.restapi.model.DDExportInfoDetail;
import com.emc.storageos.datadomain.restapi.model.DDExportList;
import com.emc.storageos.datadomain.restapi.model.DDMCInfoDetail;
import com.emc.storageos.datadomain.restapi.model.DDMTreeInfo;
import com.emc.storageos.datadomain.restapi.model.DDMTreeInfoDetail;
import com.emc.storageos.datadomain.restapi.model.DDServiceStatus;
import com.emc.storageos.datadomain.restapi.model.DDShareInfo;
import com.emc.storageos.datadomain.restapi.model.DDShareInfoDetail;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.FSExportMap;
import com.emc.storageos.db.client.model.FileExport;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.QuotaDirectory;
import com.emc.storageos.db.client.model.SMBFileShare;
import com.emc.storageos.db.client.model.SMBShareMap;
import com.emc.storageos.db.client.model.StoragePool;
import com.emc.storageos.db.client.model.StorageProvider;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.exceptions.DeviceControllerErrors;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.model.file.ExportRule;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.FileDeviceInputOutput;
import com.emc.storageos.volumecontroller.FileShareExport;
import com.emc.storageos.volumecontroller.impl.BiosCommandResult;
import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.file.AbstractFileStorageDevice;
/**
* DataDomain specific file controller implementation.
*/
public class DataDomainFileStorageDevice extends AbstractFileStorageDevice {
private static final Logger _log = LoggerFactory.getLogger(DataDomainFileStorageDevice.class);
DataDomainClientFactory _factory;
DbClient _dbClient;
/**
* Set DataDomain API factory
*
* @param factory
*/
public void setDataDomainFactory(DataDomainClientFactory factory) {
_factory = factory;
}
/**
* Set DBClient
*
* @param dbClient
*/
public void setDbClient(DbClient dbClient) {
_dbClient = dbClient;
}
/**
* Get DataDomain device represented by the StorageDevice
*
* @param device StorageDevice object
* @return DataDomainClient object
* @throws com.emc.storageos.datadomain.restapi.errorhandling.DataDomainApiException
*/
private DataDomainClient getDataDomainClient(StorageSystem device) throws DataDomainApiException {
URI providerId = device.getActiveProviderURI();
StorageProvider provider = null;
if (providerId != null) {
provider = _dbClient.queryObject(StorageProvider.class, providerId);
}
DataDomainClient ddClient = null;
if (provider != null) {
ddClient = (DataDomainClient) _factory.getRESTClient(
DataDomainApiConstants.newDataDomainBaseURI(
device.getSmisProviderIP(),
device.getSmisPortNumber()),
provider.getUserName(),
provider.getPassword());
}
return ddClient;
}
private void ddDeleteExports(DataDomainClient ddClient,
String storagePoolId,
FSExportMap currentExports,
List<FileExport> exportsToDelete) {
if ((currentExports != null && (exportsToDelete != null) && (!exportsToDelete.isEmpty()))) {
for (FileExport fileExport : exportsToDelete) {
String key = fileExport.getFileExportKey();
String ddExportId = null;
FileExport fExport = currentExports.get(key);
if (fExport != null) {
ddExportId = fExport.getNativeId();
}
if (ddExportId != null) {
DDExportInfoDetail ddExport = ddClient.getExport(storagePoolId, ddExportId);
if (ddExport.getPathStatus() == DataDomainApiConstants.PATH_EXISTS) {
DDServiceStatus ddSvcStatus = ddClient.deleteExport(storagePoolId, ddExportId);
}
}
}
}
}
private void doAddDeleteClients(DataDomainClient ddClient, String storagePoolId,
String exportId, List<ExportRule> rulesToModify, boolean delete) throws DataDomainApiException {
if ((rulesToModify != null) && (!rulesToModify.isEmpty())) {
// Build list of endpoints for rules being modified (added or deleted)
List<DDExportClient> ddExportClients = new ArrayList<>();
for (ExportRule ruleToModify : rulesToModify) {
List<DDExportClient> ddExpClients = ddBuildExportClientList(ruleToModify);
if (ddExpClients != null) {
ddExportClients.addAll(ddExpClients);
}
}
// Build list of clients to be modified on the array
List<DDExportClientModify> modifyClients = new ArrayList<>();
for (DDExportClient ddExportClient : ddExportClients) {
DDExportClientModify modifyClient = new DDExportClientModify(
ddExportClient.getName(), ddExportClient.getOptions(), delete);
modifyClients.add(modifyClient);
}
// Modify clients on the array
if (!modifyClients.isEmpty()) {
DDExportInfo ddExportInfo = ddClient.modifyExport(storagePoolId,
exportId, modifyClients);
if (ddExportInfo.getPathStatus() != DataDomainApiConstants.PATH_EXISTS) {
DDExportInfoDetail exportDetail = ddClient.getExport(storagePoolId, exportId);
if (delete) {
throw DataDomainApiException.exceptions.failedToDeleteExportClients(
exportDetail.getPath());
} else {
throw DataDomainApiException.exceptions.failedToAddExportClients(
exportDetail.getPath());
}
}
}
}
}
private void doCreateExports(DataDomainClient ddClient, String storagePoolId,
String exportPath, List<ExportRule> rulesToCreate) throws DataDomainApiException {
if ((rulesToCreate != null) && (!rulesToCreate.isEmpty())) {
// Build list of endpoints for rules being modified (added or deleted)
List<DDExportClient> ddExportClients = new ArrayList<>();
for (ExportRule ruleToCreate : rulesToCreate) {
List<DDExportClient> ddExpClients = ddBuildExportClientList(ruleToCreate);
if (ddExpClients != null) {
ddExportClients.addAll(ddExpClients);
}
}
// Create export on the array
if (!ddExportClients.isEmpty()) {
DDExportInfo ddExportInfo = ddClient.createExport(storagePoolId,
exportPath, ddExportClients);
if (ddExportInfo.getPathStatus() != DataDomainApiConstants.PATH_EXISTS) {
throw DataDomainApiException.exceptions.failedToCreateExport(
exportPath);
}
}
}
}
private void doDeleteFsExport(DataDomainClient ddClient, String storagePoolId,
String ddExportId) throws DataDomainApiException {
if (ddExportId != null) {
DDServiceStatus ddSvcStatus = ddClient.deleteExport(
storagePoolId, ddExportId);
if (ddSvcStatus.getCode() != DataDomainApiConstants.SVC_CODE_SUCCESS) {
StringBuilder message = new StringBuilder(ddSvcStatus.getCode());
message.append(": " + ddSvcStatus.getDetails());
throw DataDomainApiException.exceptions.failedToDeleteExport(
message.toString());
}
}
}
private void doDeleteExports(DataDomainClient ddClient, String storagePoolId,
List<String> exportIdsToDelete) {
if ((exportIdsToDelete != null) && (!exportIdsToDelete.isEmpty())) {
for (String exportId : exportIdsToDelete) {
DDServiceStatus ddSvcStatus = ddClient.deleteExport(
storagePoolId, exportId);
if (ddSvcStatus.getCode() != DataDomainApiConstants.SVC_CODE_SUCCESS) {
StringBuilder message = new StringBuilder(ddSvcStatus.getCode());
message.append(": " + ddSvcStatus.getDetails());
throw DataDomainApiException.exceptions.failedToDeleteExport(
message.toString());
}
}
}
}
private void ddDeleteShares(DataDomainClient ddClient,
String storagePoolId,
SMBShareMap currentShares,
List<SMBFileShare> sharesToDelete) {
if ((currentShares != null && (sharesToDelete != null))) {
for (SMBFileShare fileShare : sharesToDelete) {
String key = fileShare.getName();
String ddShareId = null;
SMBFileShare fShare = currentShares.get(key);
if (fShare != null) {
ddShareId = fShare.getNativeId();
}
if (ddShareId != null) {
DDServiceStatus ddSvcStatus = ddClient.deleteShare(storagePoolId, ddShareId);
if (ddSvcStatus.getCode() == DataDomainApiConstants.SVC_CODE_SUCCESS) {
currentShares.remove(key);
}
}
}
}
}
private List<DDExportClient> ddBuildCreateExportClientList(
FileExport fileExport) {
List<DDExportClient> ddExportClients = new ArrayList<>();
String options = ddSetExportOptions(fileExport);
List<String> addClients = fileExport.getClients();
for (String client : addClients) {
ddExportClients.add(new DDExportClient(client, options));
}
return (ddExportClients);
}
private List<DDExportClient> ddBuildExportClientList(
ExportRule exportRule) {
List<DDExportClient> ddExportClients = new ArrayList<>();
Set<String> roHosts = exportRule.getReadOnlyHosts();
Set<String> rwHosts = exportRule.getReadWriteHosts();
Set<String> rootHosts = exportRule.getRootHosts();
// Set options common to all permission types
StringBuilder commonOptions = new StringBuilder(ddSetExportOptions(exportRule));
_log.info("ddBuildExportClientList commonOptions {} for exportRule {}",
commonOptions, exportRule.toString());
// Read only exports
if (roHosts != null) {
for (String host : roHosts) {
StringBuilder roOptions = new StringBuilder(commonOptions);
roOptions.append(DataDomainApiConstants.PERMISSION_RO);
ddExportClients.add(new DDExportClient(host, roOptions.toString()));
}
}
// Read write exports
if (rwHosts != null) {
for (String host : rwHosts) {
StringBuilder rwOptions = new StringBuilder(commonOptions);
rwOptions.append(DataDomainApiConstants.PERMISSION_RW);
ddExportClients.add(new DDExportClient(host, rwOptions.toString()));
}
}
// Root exports
if (rootHosts != null) {
for (String host : rootHosts) {
StringBuilder rootOptions = new StringBuilder(commonOptions);
rootOptions.append(DataDomainApiConstants.PERMISSION_RW);
ddExportClients.add(new DDExportClient(host, rootOptions.toString()));
}
}
for (DDExportClient ddClient : ddExportClients) {
_log.info("DDExportClient : {}", ddClient.toString());
}
return (ddExportClients);
}
private List<DDExportClientModify> ddBuildModifyExportClientList(
FSExportMap exportMap, FileExport fileExport) {
List<DDExportClientModify> ddExportClients = new ArrayList<>();
String options = ddSetExportOptions(fileExport);
// Add clients from the new export
List<String> addClients = fileExport.getClients();
// Remove clients not in the new export
List<String> removeClients = new ArrayList<>();
List<String> oldClients = exportMap.get(fileExport.getFileExportKey()).getClients();
for (String oldClient : oldClients) {
if (!addClients.contains(oldClient)) {
removeClients.add(oldClient);
}
}
// Add new clients
for (String client : addClients) {
ddExportClients.add(new DDExportClientModify(client, options, false));
}
// Remove old clients no more in the list
for (String client : removeClients) {
ddExportClients.add(new DDExportClientModify(client, options, true));
}
return (ddExportClients);
}
private void ddSetNewExportProperties(FileExport fileExport,
List<DDExportClient> ddExportClients, DDExportInfo ddExportInfo) {
fileExport.setNativeId(ddExportInfo.getId());
fileExport.setMountPath(ddExportInfo.getPath());
List<String> clients = new ArrayList<>();
for (DDExportClient ddExportClient : ddExportClients) {
clients.add(ddExportClient.getName());
}
// Note: We store the fileExport permissions as available in the original
// FileExport object. Only Clients, Protocol and Security Types are updated.
fileExport.setClients(clients);
fileExport.setProtocol(DataDomainApiConstants.NFS_PROTOCOL);
String securityType = fileExport.getSecurityType();
if (securityType == null) {
fileExport.setSecurityType(DataDomainApiConstants.DEFAULT_SECURITY);
}
}
private void ddSetExistingExportProperties(FileExport existingExport,
FileExport fExport, DDExportInfo ddExportInfo) {
List<String> CurrClients = fExport.getClients();
existingExport.setClients(CurrClients);
// While storing in the ViPR DB, we need to store the unmanipulated permissions
// from FileExport object
existingExport.setPermissions(fExport.getPermissions());
existingExport.setProtocol(DataDomainApiConstants.NFS_PROTOCOL);
existingExport.setRootUserMapping(fExport.getRootUserMapping());
String securityType = fExport.getSecurityType();
if (securityType == null) {
existingExport.setSecurityType(DataDomainApiConstants.DEFAULT_SECURITY);
}
existingExport.setStoragePort(fExport.getStoragePort());
existingExport.setStoragePortName(fExport.getStoragePortName());
}
private String ddSetExportOptions(FileExport fileExport) {
StringBuilder options = new StringBuilder();
// Security type
options.append(DataDomainApiConstants.SECURITY_TYPE_OPTION +
fileExport.getSecurityType() + " ");
// Set permission, "ro" by default
options.append(ddSetPermissions(fileExport.getPermissions()) + " ");
// Root mapping
options.append(ddSetRootMappingOption(fileExport.getRootUserMapping()) + " ");
// For now map all UIDs and GIDs to anonymous
options.append(DataDomainApiConstants.ALL_SQUASH + " ");
// For now assume connections from port below 1024 are allowed
options.append(DataDomainApiConstants.SECURE + " ");
// For now assume default anonymous UID and GID are used.
// These will be set by the device.
return options.toString();
}
private String ddSetExportOptions(ExportRule exportRule) {
StringBuilder options = new StringBuilder();
// Permissions will be specified somewhere else individually for each
// client, based on the list (ro, rw, root) a client belongs to
// Security type
options.append(DataDomainApiConstants.SECURITY_TYPE_OPTION +
exportRule.getSecFlavor() + " ");
// Root mapping
options.append(ddSetRootMappingOption(exportRule.getAnon()) + " ");
// For now map all UIDs and GIDs to anonymous
options.append(DataDomainApiConstants.ALL_SQUASH + " ");
// For now assume connections from port below 1024 are allowed
options.append(DataDomainApiConstants.SECURE + " ");
// For now assume default anonymous UID and GID are used.
// These will be set by the device.
return options.toString();
}
private String ddSetPermissions(String permissions) {
if (permissions.equalsIgnoreCase(FileShareExport.Permissions.rw.toString())) {
return (FileShareExport.Permissions.rw.toString());
} else if (permissions.equalsIgnoreCase(FileShareExport.Permissions.root.toString())) {
return (FileShareExport.Permissions.rw.toString());
} else {
// Default permission on DataDomain
return (FileShareExport.Permissions.ro.toString());
}
}
private String ddSetRootMappingOption(String rootMapping) {
if (rootMapping.equalsIgnoreCase(DataDomainApiConstants.ANONYMOUS.toString())) {
return (DataDomainApiConstants.ROOT_SQUASH);
} else {
// Default setting for now
return (DataDomainApiConstants.NO_ROOT_SQUASH);
}
}
private String ddGetRootMappingOption(String rootOption) {
if (rootOption.equalsIgnoreCase(DataDomainApiConstants.NO_ROOT_SQUASH)) {
return (DataDomainApiConstants.ROOT);
} else {
// Default setting for now
return (DataDomainApiConstants.ANONYMOUS);
}
}
private void ddInitCreateModifyFileExportLists(
FSExportMap currExpMap,
List<FileExport> exportList,
List<FileExport> modifyFileExports,
List<FileExport> createFileExports) {
Set<String> currExpKeys = currExpMap.keySet();
for (FileExport fileExport : exportList) {
String newExpKey = fileExport.getFileExportKey();
if (currExpKeys.contains(newExpKey)) {
// Existing export, add to modify list
modifyFileExports.add(fileExport);
} else {
// New export, add to create list
createFileExports.add(fileExport);
}
}
}
private void ddCreateExports(DataDomainClient ddClient, String storagePoolId,
FSExportMap exportMap, List<FileExport> createFileExports)
throws DataDomainApiException {
for (FileExport fileExport : createFileExports) {
// Build export map for export create
String exportName;
if (!fileExport.getPath().startsWith(DataDomainApiConstants.FS_PATH_BASE)) {
exportName = DataDomainApiConstants.FS_PATH_BASE + fileExport.getPath();
fileExport.setPath(exportName);
} else {
exportName = fileExport.getPath();
}
List<DDExportClient> ddExportClients = ddBuildCreateExportClientList(fileExport);
for (DDExportClient ddExpClient : ddExportClients) {
_log.info("DD Export Client {}", ddExpClient.toString());
}
DDExportInfo ddExportInfo = ddClient.createExport(storagePoolId,
exportName, ddExportClients);
if (ddExportInfo.getPathStatus() != DataDomainApiConstants.PATH_EXISTS) {
DDServiceStatus ddSvcStatus = ddClient.deleteExport(
storagePoolId, ddExportInfo.getId());
throw DataDomainApiException.exceptions.failedExportPathDoesNotExist(exportName);
} else {
ddSetNewExportProperties(fileExport, ddExportClients, ddExportInfo);
String exportKey = fileExport.getFileExportKey();
exportMap.put(exportKey, fileExport);
}
}
}
private void ddModifyExports(DataDomainClient ddClient, String storagePoolId,
FSExportMap exportMap, List<FileExport> modifyFileExports) {
for (FileExport fileExport : modifyFileExports) {
fileExport.setNativeId(exportMap.get(fileExport.getFileExportKey()).getNativeId());
List<DDExportClientModify> ddExportClients = ddBuildModifyExportClientList(exportMap, fileExport);
DDExportInfoDetail ddExport = ddClient.getExport(storagePoolId,
fileExport.getNativeId());
if (ddExport.getPathStatus() != DataDomainApiConstants.PATH_EXISTS) {
DDServiceStatus ddSvcStatus = ddClient.deleteExport(
storagePoolId, ddExport.getId());
throw DataDomainApiException.exceptions.failedExportPathDoesNotExist(ddExport.getPath());
} else {
DDExportInfo ddExportInfo = ddClient.modifyExport(storagePoolId,
fileExport.getNativeId(), ddExportClients);
FileExport existingExport = exportMap.get(fileExport.getFileExportKey());
ddSetExistingExportProperties(existingExport, fileExport, ddExportInfo);
}
}
}
private void ddCreateShare(DataDomainClient ddClient, String storagePoolId,
SMBFileShare smbFileShare, String sharePath) throws DataDomainApiException {
String shareName = smbFileShare.getName();
int maxUsers = smbFileShare.getMaxUsers();
String desc = smbFileShare.getDescription();
String permissionType = smbFileShare.getPermissionType();
String permission = smbFileShare.getPermission();
DDShareInfo ddShareInfo = ddClient.createShare(storagePoolId,
shareName, sharePath, maxUsers, desc, permissionType, permission);
if (ddShareInfo.getPathStatus() != DataDomainApiConstants.PATH_EXISTS) {
DDServiceStatus ddSvcStatus = ddClient.deleteShare(storagePoolId, ddShareInfo.getId());
throw DataDomainApiException.exceptions.failedSharePathDoesNotExist(sharePath);
}
smbFileShare.setNativeId(ddShareInfo.getId());
}
@Override
public BiosCommandResult doCreateFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
_log.info("DataDomainFileStorageDevice doCreateFS {} with name {} - start",
args.getFsId(), args.getFsName());
// TODO
try {
_log.info("DataDomainFileStorageDevice doCreateFS {} with name {} - start",
args.getFsId(), args.getFsName());
DataDomainClient ddclient = getDataDomainClient(storage);
if (ddclient == null) {
_log.error("doCreateFS failed, provider unreachable");
String op = "FS create";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
// Update path and mountPath
// TODO: try to mount export
String path = args.getFsName();
String mountPath;
if (!path.startsWith(DataDomainApiConstants.FS_PATH_BASE)) {
mountPath = DataDomainApiConstants.FS_PATH_BASE + path;
} else {
mountPath = path;
}
_log.info("Mount path to mount the DataDomain File System {}", mountPath);
args.setFsMountPath(mountPath);
args.setFsPath(mountPath);
// Create MTree
// Data Domain expects capacity in Bytes
Long mtreeCapacity = args.getFsCapacity();
// TODO: Following two values are hard-coded for now, until they are implemented in UI
Boolean enableRetention = false;
String retentionMode = "compliance";
DDMTreeInfo ddMtreeInfo = ddclient.createMTree(args.getStoragePool().getNativeId(),
mountPath, mtreeCapacity, enableRetention, retentionMode);
args.setFsNativeId(ddMtreeInfo.getId());
String serialNumber = storage.getSerialNumber();
if (serialNumber == null) {
serialNumber = storage.getModel();
}
String fsNativeGuid = NativeGUIDGenerator.generateNativeGuid(
storage.getSystemType(),
serialNumber.toUpperCase(),
ddMtreeInfo.getId());
args.setFsNativeGuid(fsNativeGuid);
args.setNewFSCapacity(args.getFsCapacity());
if (args.getFsExtensions() == null) {
args.initFsExtensions();
}
args.getFsExtensions().put(DataDomainApiConstants.TOTAL_PHYSICAL_CAPACITY, String.valueOf(args.getFsCapacity()));
_log.info("DataDomainFileStorageDevice doCreateFS {} - complete", args.getFsId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doCreateFS failed, device error...attempting to delete FS to rollback.", e);
// rollback this operation to prevent partial result of file share create
BiosCommandResult rollbackResult = doDeleteFS(storage, args);
if (rollbackResult.isCommandSuccess()) {
_log.info("DataDomainFileStorageDevice doCreateFS {} - rollback completed.", args.getFsId());
} else {
_log.error("DataDomainFileStorageDevice doCreateFS {} - rollback failed, message: {} .", args.getFsId(),
rollbackResult.getMessage());
}
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doDeleteFS(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doDeleteFS {} - start",
args.getFsId());
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doDeleteFS failed, provider unreachable");
String op = "FS delete";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
// Delete the exports for this file system
FSExportMap exportMap = args.getFsExports();
List<FileExport> exportMapvalues = null;
if (exportMap != null) {
exportMapvalues = new ArrayList<>(exportMap.values());
}
if ((exportMap != null) && (exportMapvalues != null)) {
try {
ddDeleteExports(ddClient, storagePool.getNativeId(), exportMap,
exportMapvalues);
} catch (DataDomainApiException dde) {
_log.error("Unable to delete exports for the FS: ", dde);
}
}
// Delete the SMB shares for this file system
SMBShareMap shareMap = args.getFsShares();
List<SMBFileShare> shareMapValues = null;
if (shareMap != null) {
shareMapValues = new ArrayList<>(shareMap.values());
}
if ((shareMap != null) && (shareMapValues != null)) {
try {
ddDeleteShares(ddClient, storagePool.getNativeId(), shareMap,
shareMapValues);
} catch (DataDomainApiException dde) {
_log.error("Unable to delete cifs shares for the FS: ", dde);
}
}
// Delete mtree on the DD array
DDServiceStatus ddSvcStatus = ddClient.deleteMTree(
storagePool.getNativeId(), args.getFs().getNativeId());
_log.info("DataDomainFileStorageDevice doDeleteFS {} - complete", args.getFsId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doDeleteFS failed, device error", e);
return BiosCommandResult.createErrorResult(e);
} catch (DataDomainResourceNotFoundException e) {
_log.error("doDeleteFS failed, Mtree not found.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public boolean doCheckFSExists(StorageSystem storage, FileDeviceInputOutput args) throws ControllerException {
_log.info("checking file system existence on array: ", args.getFsName());
boolean isMtreeExists = true;
try {
DataDomainClient ddClient = getDataDomainClient(storage);
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class, storagePoolId);
DDMTreeInfoDetail mtreeInfo = ddClient.getMTree(storagePool.getNativeId(), args.getFs().getNativeId());
if (mtreeInfo != null && (mtreeInfo.id.equals(args.getFsNativeId()))) {
isMtreeExists = true;
}
} catch (DataDomainResourceNotFoundException e) {
_log.info("Mtree not found.", e);
isMtreeExists = false;
}
return isMtreeExists;
}
@Override
public BiosCommandResult doExpandFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doExpandFS {} - start", args.getFsId());
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doExpandFS failed, provider unreachable");
String op = "FS expand";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
Long newSize = args.getNewFSCapacity();
Long currSize;
if ((args.getFsCapacity() != null) && (args.getFsCapacity() > 0)) {
currSize = args.getFsCapacity();
} else {
ServiceError serviceError = DeviceControllerErrors.datadomain.doFailedToGetCurrSize();
return BiosCommandResult.createErrorResult(serviceError);
}
if (currSize >= newSize) {
ServiceError serviceError = DeviceControllerErrors.datadomain.doShrinkFSFailed(currSize, newSize);
return BiosCommandResult.createErrorResult(serviceError);
}
// Modify mtree
// Data Domain expects capacity in Bytes
DDMTreeInfo ddMtreeInfo = ddClient.expandMTree(
args.getStoragePool().getNativeId(), args.getFs().getNativeId(),
newSize);
if (args.getFsExtensions() == null) {
args.initFsExtensions();
}
args.getFsExtensions().put(DataDomainApiConstants.TOTAL_PHYSICAL_CAPACITY, String.valueOf(newSize));
_log.info("DataDomainFileStorageDevice doExpandFS {} - complete", args.getFsId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doExpandFS failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
} catch (Exception e) {
_log.error("doExpandFS failed.", e);
ServiceError serviceError = DeviceControllerErrors.datadomain.doExpandFSFailed(e.getMessage());
return BiosCommandResult.createErrorResult(serviceError);
}
}
@Override
public BiosCommandResult doExport(StorageSystem storage, FileDeviceInputOutput args,
List<FileExport> exportList) throws ControllerException {
_log.info("DataDomainFileStorageDevice doExport {} - start", args.getFileObjId());
// Snapshot Export operation is not supported by Data Domain.
if (args.getFileOperation() == false) {
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.doCreateSnapshotExportFailed());
}
if ((exportList == null) || (exportList.isEmpty())) {
return BiosCommandResult.createSuccessfulResult();
}
try {
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doExport failed, provider unreachable");
String op = "FS export";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
if ((args.getFsExports() == null) || (args.getFsExports().isEmpty())) {
// Initialize exports map
args.initFileObjExports();
}
// Go through the list of new exports and add each to create list
// if not in the list of existing exports or to the modify list if
// contained in the existing list
FSExportMap currExpMap = args.getFsExports();
List<FileExport> modifyFileExports = new ArrayList<>();
List<FileExport> createFileExports = new ArrayList<>();
ddInitCreateModifyFileExportLists(currExpMap, exportList,
modifyFileExports, createFileExports);
// Create new exports and add to file export map
ddCreateExports(ddClient, args.getStoragePool().getNativeId(),
args.getFsExports(), createFileExports);
// Modify existing exports
ddModifyExports(ddClient, args.getStoragePool().getNativeId(),
args.getFsExports(), modifyFileExports);
_log.info("DataDomainFileStorageDevice doExport {} - complete", args.getFileObjId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doExport failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doUnexport(StorageSystem storage,
FileDeviceInputOutput args,
List<FileExport> exportList) throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doUnexport {} - start",
args.getFsId());
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doUnexport failed, provider unreachable");
String op = "FS unexport";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
FSExportMap currentExports = args.getFsExports();
ddDeleteExports(ddClient, storagePool.getNativeId(), currentExports,
exportList);
_log.info("DataDomainFileStorageDevice doUnexport {} - complete",
args.getFsId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doUnexport failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doShare(StorageSystem storage, FileDeviceInputOutput args,
SMBFileShare smbFileShare) throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doShare() - start");
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doShare failed, provider unreachable");
String op = "FS share create";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
// Check if this is a new share or update of the existing share
SMBShareMap smbShareMap = args.getFileObjShares();
SMBFileShare existingShare = (smbShareMap == null) ? null : smbShareMap.get(smbFileShare.getName());
String shareId;
DDShareInfo ddShareInfo;
// Cannot send empty description, send the share name in that case
if (smbFileShare.getDescription() == null || smbFileShare.getDescription().isEmpty()) {
_log.debug("SMB Share creation was called with empty description and setting name as desc");
smbFileShare.setDescription(smbFileShare.getName());
}
if (existingShare != null) {
shareId = existingShare.getNativeId();
// modify share
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(
StoragePool.class, storagePoolId);
DDShareInfoDetail ddShareInfoDetail = ddClient.getShare(storagePool.getNativeId(), shareId);
if (ddShareInfoDetail.getPathStatus() == 0) {
DDServiceStatus ddSvcStatus = ddClient.deleteShare(storagePool.getNativeId(), shareId);
throw DataDomainApiException.exceptions.failedSharePathDoesNotExist(ddShareInfoDetail.getPath());
}
ddShareInfo = ddClient.modifyShare(
storagePool.getNativeId(),
shareId, smbFileShare.getDescription());
} else {
// new share
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(
StoragePool.class, storagePoolId);
ddCreateShare(ddClient, storagePool.getNativeId(),
smbFileShare, smbFileShare.getPath());
}
// init file share map
if (args.getFileObjShares() == null) {
args.initFileObjShares();
}
args.getFileObjShares().put(smbFileShare.getName(), smbFileShare);
_log.info("DataDomainFileStorageDevice doShare() - complete");
// Set MountPoint
smbFileShare.setMountPoint(smbFileShare.getStoragePortNetworkId(), smbFileShare.getStoragePortName(),
smbFileShare.getName());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doShare failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doDeleteShare(StorageSystem storage,
FileDeviceInputOutput args, SMBFileShare smbFileShare)
throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doDeleteShare: {} - start");
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doDeleteShare failed, provider unreachable");
String op = "FS share delete";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
SMBShareMap currentShares = args.getFileObjShares();
List<SMBFileShare> sharesToDelete = new ArrayList<SMBFileShare>();
sharesToDelete.add(smbFileShare);
ddDeleteShares(ddClient, storagePool.getNativeId(),
currentShares, sharesToDelete);
_log.info("DataDomainFileStorageDevice doDeleteShare {} - complete");
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doDeleteShare failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doDeleteShares(StorageSystem storage, FileDeviceInputOutput args) throws ControllerException {
try {
_log.info("DataDomainFileStorageDevice doDeleteShares: {} - start");
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doDeleteShares failed, provider unreachable");
String op = "FS shares delete";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
SMBShareMap currentShares = args.getFileObjShares();
List<SMBFileShare> sharesToDelete = new ArrayList<SMBFileShare>();
sharesToDelete.addAll(currentShares.values());
ddDeleteShares(ddClient, storagePool.getNativeId(),
currentShares, sharesToDelete);
_log.info("DataDomainFileStorageDevice doDeleteShare {} - complete");
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException e) {
_log.error("doDeleteShare failed, device error.", e);
return BiosCommandResult.createErrorResult(e);
}
}
@Override
public BiosCommandResult doModifyFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
result.setCommandSuccess(false);
result.setCommandStatus(Operation.Status.error.name());
result.setMessage("Modify FS NOT supported for DataDomain.");
return result;
}
@Override
public BiosCommandResult doSnapshotFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
String message = "Snapshot creation on Data Domain not supported yet, create operation failed";
_log.error(message);
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
serviceError.setMessage(message);
serviceError.setRetryable(false);
return BiosCommandResult.createErrorResult(serviceError);
// TODO Following lines may be uncommented once Data domain provides snapshot APIs
// try {
// _log.info("DataDomainFileStorageDevice doSnapshotFS {} {} - start",
// args.getSnapshotId(), args.getSnapshotName());
// DataDomainClient ddClient = getDataDomainClient(storage);
// // To Do - add timestamp for uniqueness
// DDSnapshotCreate snapshotCreate = new DDSnapshotCreate(
// args.getSnapshotName(), args.getFsMountPath());
// // TODO: Following will be uncommented when DD API becomes available.
// DDSnapshot snapshot = ddClient.createSnapshot(
// args.getStoragePool().getNativeId(), snapshotCreate);
// if (args.getSnapshotExtensions() == null) {
// args.initSnapshotExtensions();
// }
// // TODO: These are placeholders until DD APIs are implemented
// String id = "fictitiousId";
// String name = "fictitiousName";
// String path = "fictitiousPath";
// DDSnapshot snapshot = new DDSnapshot(id, name, path);
// // Remove lines above
//
// args.setSnapNativeId(snapshot.getId());
// // For now Data Domain does not support snapshot export
// args.setSnapshotMountPath(snapshot.getPath());
// args.setSnapshotPath(snapshot.getPath());
// _log.info("DataDomainFileStorageDevice doSnapshotFS {} - complete",
// args.getSnapshotId());
// return BiosCommandResult.createSuccessfulResult();
// } catch (DataDomainApiException e) {
// _log.error("doSnapshotFS failed, device error. ", e);
// return BiosCommandResult.createErrorResult(e);
// }
}
@Override
public BiosCommandResult doRestoreFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
// TODO To be implemented once Data Domain provides snapshot APIs
return BiosCommandResult.createSuccessfulResult();
}
@Override
public BiosCommandResult doDeleteSnapshot(StorageSystem storage,
FileDeviceInputOutput args)
throws ControllerException {
String message = "Data Domain snapshots not supported yet, delete operation failed";
_log.error(message);
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
serviceError.setMessage(message);
serviceError.setRetryable(false);
return BiosCommandResult.createErrorResult(serviceError);
// TODO Uncomment the following lines once Data Domain provides snapshot APIs
// if (args.getFsName() == null) {
// return BiosCommandResult.createErrorResult(
// DeviceControllerErrors.datadomain.
// doDeleteSnapshotFailedNoFSName());
// }
//
// if (args.getSnapshotName() == null) {
// return BiosCommandResult.createErrorResult(
// DeviceControllerErrors.datadomain.
// doDeleteSnapshotFailedNoSnapName());
// }
// try{
// _log.info("DataDomainFileStorageDevice doDeleteSnapshot {} - start",
// args.getSnapshotId());
// DataDomainClient ddClient = getDataDomainClient(storage);
//
// // Data Domain does not support exports or shares on snapshots.
// // Just delete snapshot
// String ddSnapshotId = args.getSnapNativeId();
// URI storagePoolId = args.getFs().getPool();
// StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
// storagePoolId);
// // TODO: Following will be uncommented when DD API becomes available.
// DDServiceStatus svcStatus = ddClient.deleteSnapshot(
// storagePool.getNativeId(), ddSnapshotId);
// _log.info("DataDomainFileStorageDevice doDeleteSnapshot {} - complete",
// args.getSnapshotId());
// return BiosCommandResult.createSuccessfulResult();
// } catch (DataDomainApiException e) {
// _log.error("doDeleteSnapshot failed.", e);
// return BiosCommandResult.createErrorResult(e);
// }
}
// Get FS snapshot list from the array
@Override
public BiosCommandResult getFSSnapshotList(StorageSystem storage,
FileDeviceInputOutput args, List<String> snapshots)
throws ControllerException {
// TODO To be implemented once Data Domain provides snapshot APIs
String message = "Data Domain snapshots not supported yet, get list operation failed";
_log.error(message);
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
serviceError.setMessage(message);
serviceError.setRetryable(false);
return BiosCommandResult.createErrorResult(serviceError);
}
@Override
public void doConnect(StorageSystem storage) {
try {
_log.info("doConnect {} - start", storage.getId());
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("doConnect failed, provider unreachable");
String sys = storage.getLabel() + "(" + storage.getIpAddress()
+ ")";
throw DataDomainApiException.exceptions.connectStorageFailed(sys);
}
DDMCInfoDetail ddInfo = ddClient.getManagementSystemInfo();
String msg = String.format("doConnect %1$s - complete", ddInfo);
_log.info(msg);
} catch (DataDomainApiException e) {
_log.error("doConnect failed.", e);
throw DeviceControllerException.exceptions.connectStorageFailed(e);
}
}
@Override
public void doDisconnect(StorageSystem storage) {
// not much to do here ... just reply success
}
@Override
public BiosCommandResult getPhysicalInventory(StorageSystem storage) {
// TODO
return null;
}
@Override
public BiosCommandResult doCreateQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args, QuotaDirectory qd) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
@Override
public BiosCommandResult doDeleteQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
@Override
public BiosCommandResult doUpdateQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args, QuotaDirectory qd) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
ServiceError serviceError = DeviceControllerErrors.datadomain.operationNotSupported();
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
private boolean repeatedClients(List<String> clients,
List<ExportRule> exportRules) {
if ((exportRules != null) && (!exportRules.isEmpty())) {
for (ExportRule expRule : exportRules) {
if (expRule.getReadOnlyHosts() != null) {
for (String client : expRule.getReadOnlyHosts()) {
if (clients.contains(client)) {
return true;
} else {
clients.add(client);
}
}
}
if (expRule.getReadWriteHosts() != null) {
for (String client : expRule.getReadWriteHosts()) {
if (clients.contains(client)) {
return true;
} else {
clients.add(client);
}
}
}
if (expRule.getRootHosts() != null) {
for (String client : expRule.getRootHosts()) {
if (clients.contains(client)) {
return true;
} else {
clients.add(client);
}
}
}
}
}
return false;
}
private boolean addingExistingClient(List<ExportRule> existingExports,
String exportPath, List<ExportRule> exportAdd) {
List<String> clients = new ArrayList<>();
if ((existingExports != null) && (!existingExports.isEmpty()) &&
(exportAdd != null) && (!exportAdd.isEmpty())) {
for (ExportRule exportRule : existingExports) {
if (exportRule.getExportPath().equals(exportPath)) {
if (exportRule.getReadOnlyHosts() != null) {
clients.addAll(exportRule.getReadOnlyHosts());
}
if (exportRule.getReadWriteHosts() != null) {
clients.addAll(exportRule.getReadWriteHosts());
}
if (exportRule.getRootHosts() != null) {
clients.addAll(exportRule.getRootHosts());
}
}
}
if (repeatedClients(clients, exportAdd)) {
return true;
}
}
return false;
}
private boolean repeatedClientsInRequest(List<ExportRule> exportAdd,
List<ExportRule> exportDelete, List<ExportRule> exportModify) {
List<String> clients = new ArrayList<>();
if (repeatedClients(clients, exportAdd)) {
return true;
}
if (repeatedClients(clients, exportDelete)) {
return true;
}
if (repeatedClients(clients, exportModify)) {
return true;
}
return false;
}
@Override
public BiosCommandResult updateExportRules(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
// Requested Export Rules
List<ExportRule> exportAdd = args.getExportRulesToAdd();
List<ExportRule> exportDelete = args.getExportRulesToDelete();
List<ExportRule> exportModify = args.getExportRulesToModify();
// Export path
String exportPath;
String subDir = args.getSubDirectory();
StringBuilder path = new StringBuilder();
if (!args.getFileOperation()) {
path.append(args.getSnapshotPath());
} else {
path.append(args.getFs().getPath());
}
if ((subDir != null) && (subDir.length() > 0)) {
path.append("/");
path.append(subDir);
}
exportPath = path.toString();
// Data Domain attaches a prefix to every file system path
path = new StringBuilder();
if (!exportPath.startsWith(DataDomainApiConstants.FS_PATH_BASE)) {
path.append(DataDomainApiConstants.FS_PATH_BASE);
}
path.append(exportPath);
_log.info("exportPath : {}", path);
args.setExportPath(path.toString());
// Check to ensure clients are not repeated
if (repeatedClientsInRequest(exportAdd, exportDelete, exportModify)) {
ServiceError serviceError = DeviceControllerErrors.datadomain
.exportUpdateFailedRepeatedClients();
return BiosCommandResult.createErrorResult(serviceError);
}
// Ensure that the clients being added don't already exist in
// another export rule
List<ExportRule> existingExports = args.getExistingDBExportRules();
if (addingExistingClient(existingExports, path.toString(), exportAdd)) {
ServiceError serviceError = DeviceControllerErrors.datadomain
.exportUpdateFailedAddingExistingClient();
return BiosCommandResult.createErrorResult(serviceError);
}
// To be processed export rules
List<ExportRule> exportsToRemove = new ArrayList<>();
List<ExportRule> exportsToAdd = new ArrayList<>();
if (existingExports != null && !existingExports.isEmpty()) {
args.setObjIdOnDevice(existingExports.get(0).getDeviceExportId());
_log.info("Number of existng Rules found {}", existingExports.size());
}
// Create/Modify/Delete exports on the array
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("updateExportRules failed, provider unreachable");
String op = "Update export rules";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
// Process modify list
// If all existing export rules are being modified, all corresponding
// end points must be deleted first then recreated. This requires
// deleting the export on the array.
// If not all existing export rules are being modified, end points
// may be deleted selectively without deleting the export.
if ((exportModify != null) && (!exportModify.isEmpty())) {
// Are all existing rules being modified?
int existingRulesToModify = 0;
int numExistingRules = 0;
for (ExportRule modifyRule : exportModify) {
String securityFlavor = modifyRule.getSecFlavor();
for (ExportRule existingRule : existingExports) {
if (existingRule.getExportPath().equals(path.toString())) {
if (existingRule.getSecFlavor().equals(securityFlavor)) {
existingRulesToModify++;
}
numExistingRules++;
}
}
}
if (existingRulesToModify == numExistingRules) {
// All rules are being modified, delete the existing export on the array
String deviceExportId = null;
for (ExportRule existingRule : existingExports) {
if (existingRule.getExportPath().equals(path.toString())) {
deviceExportId = existingRule.getDeviceExportId();
break;
}
}
try {
doDeleteFsExport(ddClient, storagePool.getNativeId(), deviceExportId);
} catch (DataDomainApiException dde) {
_log.error("Export update failed, device error.", dde);
return BiosCommandResult.createErrorResult(dde);
}
// Export rules to create
for (ExportRule modifyRule : exportModify) {
exportsToAdd.add(modifyRule);
}
doCreateExports(ddClient, storagePool.getNativeId(), path.toString(),
exportsToAdd);
exportsToAdd.clear();
} else {
// Not all are being modified, modify existing export by deleting
// end points selectively
for (ExportRule modifyRule : exportModify) {
String securityFlavor = modifyRule.getSecFlavor();
ExportRule matchingRule = null;
for (ExportRule existingRule : existingExports) {
if (existingRule.getSecFlavor().equals(securityFlavor)) {
matchingRule = existingRule;
break;
}
}
// Since the list has been validated already, we can safely assume we will
// always find a matching existing rule.
modifyRule.setExportPath(args.getExportPath());
modifyRule.setFsID(args.getFsId());
exportsToRemove.add(matchingRule);
exportsToAdd.add(modifyRule);
}
}
}
// Process add list
if ((exportAdd != null) && (!exportAdd.isEmpty())) {
for (ExportRule newExport : exportAdd) {
_log.info("Adding Export Rule {}", newExport);
if (args.getFileObjExports() != null) {
Collection<FileExport> expList = args.getFileObjExports().values();
Iterator<FileExport> it = expList.iterator();
FileExport exp = null;
while (it.hasNext()) {
FileExport export = it.next();
if (export.getPath().equalsIgnoreCase(path.toString())) {
exp = export;
}
}
// set the device export id with export id.
if (exp != null) {
if (exp.getIsilonId() != null) {
newExport.setDeviceExportId(exp.getIsilonId());
}
if (exp.getNativeId() != null) {
newExport.setDeviceExportId(exp.getNativeId());
}
}
}
newExport.setExportPath(args.getExportPath());
newExport.setFsID(args.getFsId());
exportsToAdd.add(newExport);
}
// If there are no existing rules, create export.
// otherwise, update the exports.
if (existingExports == null || existingExports.isEmpty()) {
doCreateExports(ddClient, storagePool.getNativeId(), path.toString(),
exportsToAdd);
exportsToAdd.clear();
}
}
// Process delete list
// If all existing rules are being deleted, simply delete the FS export.
// If not all rules are being deleted, update export by deleting end points
// selectively, without disrupting unaffected end points.
if ((exportDelete != null) && (!exportDelete.isEmpty())) {
// Are all existing rules being deleted?
int existingRulesToDelete = 0;
for (ExportRule deleteRule : exportDelete) {
String securityFlavor = deleteRule.getSecFlavor();
for (ExportRule existingRule : existingExports) {
if (existingRule.getSecFlavor().equals(securityFlavor)) {
existingRulesToDelete++;
}
}
}
if (existingRulesToDelete == existingExports.size()) {
// All rules are being deleted, delete the existing export on the array
String deviceExportId = existingExports.get(0).getDeviceExportId();
try {
doDeleteFsExport(ddClient, storagePool.getNativeId(), deviceExportId);
} catch (DataDomainApiException dde) {
_log.error("Export update failed, device error.", dde);
return BiosCommandResult.createErrorResult(dde);
}
} else {
// Not all rules are being deleted, modify existing export by deleting
// end points selectively
for (ExportRule deleteRule : exportDelete) {
String securityFlavor = deleteRule.getSecFlavor();
ExportRule matchingRule = null;
for (ExportRule existingRule : existingExports) {
if (existingRule.getSecFlavor().equals(securityFlavor)) {
matchingRule = existingRule;
break;
}
}
// Since the list has been validated already, we can safely assume we will
// always find a matching existing rule.
exportsToRemove.add(matchingRule);
}
}
}
_log.info("No of exports to be removed from the existing list {}", exportsToRemove.size());
_log.info("No of exports to be added to the existing list {}", exportsToAdd.size());
// Delete clients selectively
try {
String deviceExportId = null;
for (ExportRule existingRule : existingExports) {
if (existingRule.getExportPath().equals(path.toString())) {
deviceExportId = existingRule.getDeviceExportId();
break;
}
}
boolean deleteClients = true;
doAddDeleteClients(ddClient, storagePool.getNativeId(), deviceExportId,
exportsToRemove, deleteClients);
} catch (DataDomainApiException dde) {
_log.error("Export update failed, device error.", dde);
return BiosCommandResult.createErrorResult(dde);
}
// Create exports
try {
String deviceExportId = null;
for (ExportRule existingRule : existingExports) {
if (existingRule.getExportPath().equals(path.toString())) {
deviceExportId = existingRule.getDeviceExportId();
break;
}
}
boolean deleteClients = false;
doAddDeleteClients(ddClient, storagePool.getNativeId(), deviceExportId,
exportsToAdd, deleteClients);
} catch (DataDomainApiException dde) {
_log.error("Export update failed, device error.", dde);
return BiosCommandResult.createErrorResult(dde);
}
_log.info("DataDomainFileStorageDevice updateFSExportRules {} - complete",
args.getFsId());
return BiosCommandResult.createSuccessfulResult();
}
@Override
public BiosCommandResult deleteExportRules(StorageSystem storage,
FileDeviceInputOutput args) {
try {
// TODO - These lines may be removed once DD snapshot APIs become available.
if (!args.getFileOperation()) {
// Snapshot Export operation is not supported by Data Domain.
ServiceError serviceError = DeviceControllerErrors.datadomain
.operationNotSupported();
serviceError.setMessage("Data Domain does not support snapshot export");
return BiosCommandResult.createErrorResult(serviceError);
}
_log.info("DataDomainFileStorageDevice deleteExportRules - start");
FileShare fs = args.getFs();
// List of existing export rules
List<ExportRule> existingExports = args.getExistingDBExportRules();
if ((existingExports == null) || (existingExports.isEmpty())) {
_log.info(
"Export rule delete, file system {} does not have an existing export to delete",
args.getFsId());
return BiosCommandResult.createSuccessfulResult();
}
_log.info("Number of existng Rules found {}", existingExports.size());
// Build export path, adding sub-directory if not null and non-empty
String exportPath;
String subDir = args.getSubDirectory();
StringBuilder expPath = new StringBuilder();
if (!args.getFileOperation()) {
expPath.append(args.getSnapshotPath());
} else {
expPath.append(args.getFs().getPath());
}
if ((subDir != null) && (subDir.length() > 0)) {
expPath.append("/");
expPath.append(subDir);
}
exportPath = expPath.toString();
// Data Domain attaches a prefix to every file system path
expPath = new StringBuilder();
if (!exportPath.startsWith(DataDomainApiConstants.FS_PATH_BASE)) {
expPath.append(DataDomainApiConstants.FS_PATH_BASE);
}
expPath.append(exportPath);
_log.info("exportPath : {}", expPath);
args.setExportPath(expPath.toString());
for (ExportRule expRule : existingExports) {
if (expRule.getExportPath().equals(expPath.toString())) {
args.setObjIdOnDevice(expRule.getDeviceExportId());
break;
}
}
// Do we need to delete all subdirectories as well?
boolean allDirs = args.isAllDir();
// List of IDs of exports to delete
List<String> exportIdsToDelete = new ArrayList<>();
exportIdsToDelete.add(args.getObjIdOnDevice());
DataDomainClient ddClient = getDataDomainClient(storage);
if (ddClient == null) {
_log.error("deleteExportRules failed, provider unreachable");
String op = "Delete export rules";
return BiosCommandResult.createErrorResult(DeviceControllerErrors.datadomain.operationFailedProviderInaccessible(op));
}
URI storagePoolId = args.getFs().getPool();
StoragePool storagePool = _dbClient.queryObject(StoragePool.class,
storagePoolId);
if (allDirs) {
// Add to the list of IDs of exports to delete
buildListOfIdsToDelete(ddClient, storagePool.getNativeId(), expPath.toString(), exportIdsToDelete);
}
doDeleteExports(ddClient, storagePool.getNativeId(), exportIdsToDelete);
_log.info("DataDomainFileStorageDevice deleteExportRules {} - complete",
args.getFsId());
return BiosCommandResult.createSuccessfulResult();
} catch (DataDomainApiException dde) {
_log.error("Export update failed, device error:", dde);
return BiosCommandResult.createErrorResult(dde);
}
}
private void buildListOfIdsToDelete(DataDomainClient ddClient, String system, String exportPath,
List<String> exportIdsToDelete) {
// Get the list of all exports from the array, select those for deletion that
// have a path that starts with exportPath.
DDExportList exportList = ddClient.getExports(system);
if ((exportList != null) && (exportList.getExports() != null) && (!exportList.getExports().isEmpty())) {
for (DDExportInfo exportInfo : exportList.getExports()) {
if ((exportInfo.getPath().startsWith(exportPath)) &&
(!exportIdsToDelete.contains(exportInfo.getId()))) {
exportIdsToDelete.add(exportInfo.getId());
}
}
}
}
@Override
public BiosCommandResult updateShareACLs(StorageSystem storage,
FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult deleteShareACLs(StorageSystem storageObj,
FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult updateNfsACLs(StorageSystem storage, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult deleteNfsACLs(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult assignFilePolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult unassignFilePolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult listSanpshotByPolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult updateStorageSystemFileProtectionPolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
@Override
public BiosCommandResult checkFilePolicyPathHasResourceLabel(StorageSystem system, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.datadomain.operationNotSupported());
}
}