/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.netapp;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Snapshot;
import com.emc.storageos.db.client.model.StorageHADomain;
import com.emc.storageos.db.client.model.StoragePort;
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.model.file.ShareACL;
import com.emc.storageos.netapp.NetAppApi;
import com.emc.storageos.netapp.NetAppException;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.util.FileSystemConstants;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.FileDeviceInputOutput;
import com.emc.storageos.volumecontroller.impl.BiosCommandResult;
import com.emc.storageos.volumecontroller.impl.NativeGUIDGenerator;
import com.emc.storageos.volumecontroller.impl.file.AbstractFileStorageDevice;
import com.iwave.ext.netapp.model.CifsAccess;
import com.iwave.ext.netapp.model.CifsAcl;
public class NetAppFileStorageDevice extends AbstractFileStorageDevice {
private static final Logger _log = LoggerFactory
.getLogger(NetAppFileStorageDevice.class);
private DbClient _dbClient;
private static int BYTESPERMB = 1048576;
private static final String VOL_ROOT = "/vol/";
private static final String VOL_ROOT_NO_SLASH = "/vol";
private static final String UNIX_QTREE_SETTING = "unix";
private static final String NTFS_QTREE_SETTING = "ntfs";
private static final String RO_PERM = "ro";
private static final String RW_PERM = "rw";
private static final String ROOT_PERM = "root";
private static final String RO_HOSTS = "roHosts";
private static final String ROOT_HOSTS = "rootHosts";
private static final String RW_HOSTS = "rwHosts";
private static final int QUOTA_DIR_MAX_PATH = 100;
private static final int QUOTA_DIR_MAX_NAME = 64;
public NetAppFileStorageDevice() {
}
public void setDbClient(DbClient dbc) {
_dbClient = dbc;
}
private String genDetailedMessage(String methodName, String entityName) {
StringBuilder detailedMessage = new StringBuilder("NetAppFileStorageDevice ");
detailedMessage.append(methodName);
detailedMessage.append(" failed for ");
detailedMessage.append(entityName);
return detailedMessage.toString();
}
@Override
public BiosCommandResult doCreateFS(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doCreateFS - start");
if (null == args.getFsName()) {
_log.error("NetAppFileStorageDevice::doCreateFS failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == args.getPoolNativeId()) {
_log.error("NetAppFileStorageDevice::doCreateFS failed: PoolNativeId either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_POOL_NATIVE_ID_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == args.getFsCapacity()) {
_log.error("NetAppFileStorageDevice::doCreateFS failed: Filesystem capacity is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_CAPACITY_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
String nativeId;
if (args.getFsName().startsWith(VOL_ROOT)) {
nativeId = args.getFsName();
} else {
nativeId = VOL_ROOT + args.getFsName();
}
args.setFsNativeId(nativeId);
String fsNativeGuid = NativeGUIDGenerator.generateNativeGuid(
storage.getSystemType(), storage.getSerialNumber(),
nativeId);
args.setFsNativeGuid(fsNativeGuid);
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
Long fsSize = args.getFsCapacity() / BYTESPERMB;
String strFsSize = fsSize.toString() + "m";
if (!nApi.createFS(args.getFsName(), args.getPoolNativeId(),
strFsSize, args.getThinProvision())) {
_log.error("NetAppFileStorageDevice doCreateFS {} - failed", args.getFsName());
BiosCommandResult rollbackResult = doDeleteFS(storage, args);
if (rollbackResult.isCommandSuccess()) {
_log.info(
"NetAppFileStorageDevice doCreateFS rollback completed failed for fs, {}", args.getFsName());
} else {
_log.error(
"NetAppFileStorageDevice doCreateFS rollback failed for fs, {} with {}.",
args.getFsName(), rollbackResult.getMessage());
}
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("NetAppFileStorageDevice doCreateFS {} - complete",
args.getFsName());
// Set FS path and Mount Path information
args.setFsPath(nativeId);
args.setFsMountPath(nativeId);
result = BiosCommandResult.createSuccessfulResult();
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doCreateFS failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doCreateFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
/**
* Deleting a file system: - deletes FileSystem and any correponding exports, smb shares
*
* @param StorageSystem storage
* @param args FileDeviceInputOutput
* @return BiosCommandResult
* @throws ControllerException
*/
@Override
public BiosCommandResult doDeleteFS(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doDeleteFS - start");
if (null == args.getFsName()) {
_log.error("NetAppFileStorageDevice::doDeletFS failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileSystem();
serviceError.setMessage("Filesystem name is either missing or empty");
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
FileShare fileshare = args.getFs();
// Now get the VFiler from the fileShare
String portGroup = findVfilerName(fileshare);
if (null != args.getFsShares() && !args.getFsShares().isEmpty()) {
if (!netAppDeleteCIFSExports(storage, args.getFsShares(), portGroup)) {
_log.info("NetAppFileStorageDevice doDeletFS:netAppDeleteCIFSExports {} - failed", args.getFsName());
} else {
_log.info("NetAppFileStorageDevice doDeletFS:netAppDeleteCIFSExports {} - succeeded", args.getFsName());
}
}
boolean failedStatus = false;
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
if (!nApi.deleteFS(args.getFsName())) {
failedStatus = true;
}
if (failedStatus == true) {
_log.error("NetAppFileStorageDevice doDeletFS {} - failed",
args.getFsName());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileSystem();
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("NetAppFileStorageDevice doDeletFS {} - complete",
args.getFsName());
result = BiosCommandResult.createSuccessfulResult();
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doDeleteFS failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doDeleteFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
/**
* Checking a file system: - Check if the FS exists on Array or not
*
* @param StorageSystem storage
* @param args FileDeviceInputOutput
* @return boolean true if exists else false
* @throws ControllerException
*/
@Override
public boolean doCheckFSExists(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
_log.info("checking file system existence on array: ", args.getFsName());
boolean isFSExists = true;
try {
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
List<String> fs = nApi.listFileSystems();
if (!fs.isEmpty() && fs.contains(args.getFsName())) {
isFSExists = true;
} else {
isFSExists = false;
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doCheckFSExists failed with an Exception", e);
}
return isFSExists;
}
private Boolean netAppDeleteNFSExports(StorageSystem storage,
FSExportMap exportMap) throws NetAppException {
int failedCount = 0;
Iterator<Map.Entry<String, FileExport>> it = exportMap.entrySet()
.iterator();
while (it.hasNext()) {
Map.Entry<String, FileExport> entry = it.next();
String key = entry.getKey();
FileExport fsExport = entry.getValue();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
if (!nApi.deleteNFS(fsExport.getPath())) {
failedCount++;
} else {
exportMap.remove(key);
}
}
if (failedCount > 0) {
return false;
} else {
return true;
}
}
private Boolean netAppDeleteCIFSExports(StorageSystem storage,
SMBShareMap currentShares, String portGroup) throws NetAppException {
int failedCount = 0;
Iterator<Entry<String, SMBFileShare>> it = currentShares.entrySet()
.iterator();
List<String> removedShareKeys = new ArrayList<String>();
while (it.hasNext()) {
Map.Entry<String, SMBFileShare> entry = it.next();
String key = entry.getKey();
SMBFileShare smbFileShare = entry.getValue();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
if (!nApi.deleteShare(smbFileShare.getName())) {
failedCount++;
} else {
removedShareKeys.add(key);
}
}
for (String keys : removedShareKeys) {
currentShares.remove(keys);
}
if (failedCount > 0) {
return false;
} else {
return true;
}
}
@Override
public BiosCommandResult doExport(StorageSystem storage,
FileDeviceInputOutput args, List<FileExport> exportList)
throws ControllerException {
_log.info("NetAppFileStorageDevice doExport - start");
// Verify inputs.
validateExportArgs(exportList);
List<String> rootHosts = new ArrayList<String>();
List<String> rwHosts = new ArrayList<String>();
List<String> roHosts = new ArrayList<String>();
if (args.getFileObjExports() == null
|| args.getFileObjExports().isEmpty()) {
args.initFileObjExports();
}
FSExportMap existingExpMap = args.getFileObjExports();
List<FileExport> existingExportList = new ArrayList<FileExport>();
FileExport existingExport = null;
Iterator<String> it = existingExpMap.keySet().iterator();
while (it.hasNext()) {
existingExport = existingExpMap.get(it.next());
_log.info("Existing export FileExport key : {} ",
existingExport.getFileExportKey());
existingExportList.add(existingExport);
}
// If it's a sub-directory no need to take existing hosts.
boolean isSubDir = checkIfSubDirectory(args.getFsMountPath(), exportList.get(0).getMountPath());
if (isSubDir) {
existingExportList = null;
}
// TODO: Revisit once new Data Model for Exports is implemented.
Map<String, List<String>> existingHosts = null;
if ((null != existingExportList) && !existingExportList.isEmpty()) {
existingHosts = sortHostsFromCurrentExports(existingExportList);
}
if (null != existingHosts) {
if ((null != existingHosts.get(ROOT_HOSTS))
&& !existingHosts.get(ROOT_HOSTS).isEmpty()) {
addNewHostsOnly(rootHosts, existingHosts.get(ROOT_HOSTS));
}
if ((null != existingHosts.get(RW_HOSTS))
&& !existingHosts.get(RW_HOSTS).isEmpty()) {
addNewHostsOnly(rwHosts, existingHosts.get(RW_HOSTS));
}
if ((null != existingHosts.get(RO_HOSTS))
&& !existingHosts.get(RO_HOSTS).isEmpty()) {
addNewHostsOnly(roHosts, existingHosts.get(RO_HOSTS));
}
}
BiosCommandResult result = new BiosCommandResult();
try {
for (int expCount = 0; expCount < exportList.size(); expCount++) {
FileExport export = exportList.get(expCount);
if (!(export.getMountPath().startsWith(VOL_ROOT_NO_SLASH))) {
export.setMountPath(VOL_ROOT_NO_SLASH
+ export.getMountPath());
}
FileExport fileExport = new FileExport(export.getClients(),
export.getStoragePortName(), export.getMountPoint(),
export.getSecurityType(), export.getPermissions(),
export.getRootUserMapping(), export.getProtocol(),
export.getStoragePort(), export.getPath(),
export.getMountPath(), export.getSubDirectory(), export.getComments());
args.getFileObjExports().put(fileExport.getFileExportKey(),
fileExport);
String portGroup = null;
FileShare fileshare = null;
if (args.getFileOperation() == true) {
fileshare = args.getFs();
portGroup = findVfilerName(fileshare);
} else {
// Get the FS from the snapshot
URI snapShotUID = args.getSnapshotId();
Snapshot snapshot = _dbClient.queryObject(Snapshot.class, snapShotUID);
fileshare = _dbClient.queryObject(FileShare.class, snapshot.getParent().getURI());
// Now get the VFiler from the fileshare
portGroup = findVfilerName(fileshare);
}
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
List<String> endpointsList = export.getClients();
if (endpointsList == null) {
_log.error("NetAppFileStorageDevice::doExport {} failed: No endpoints specified", args.getFsId());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExportFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_NO_ENDPOINTS_SPECIFIED);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
sortNewEndPoints(rootHosts, rwHosts, roHosts, endpointsList,
export.getPermissions());
String root_user = export.getRootUserMapping();
String mountPath = export.getMountPath();
String exportPath = export.getPath();
if (!nApi.exportFS(exportPath, mountPath, rootHosts, rwHosts,
roHosts, root_user, export.getSecurityType())) {
_log.error("NetAppFileStorageDevice::doExport {} failed", args.getFsId());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExportFileSystem();
serviceError.setMessage(genDetailedMessage("doExport", args.getFsId().toString()));
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
result = BiosCommandResult.createSuccessfulResult();
if ((args.getFileOperation() == true) && (isSubDir == false)) {
nApi.setQtreemode(exportPath, UNIX_QTREE_SETTING);
}
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doExport failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExportFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doExport failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExportFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
_log.info("NetAppFileStorageDevice::doExport {} - complete", args.getFsId());
return result;
}
private void addNewHostsOnly(List<String> permHosts, List<String> newHosts) {
if (null != newHosts) {
for (String newHost : newHosts) {
if (!(permHosts.contains(newHost))) {
permHosts.add(newHost);
}
}
}
}
/*
* We check to see if fsMountPath is same as exportMountPath, if not we know that it's a sub-directory
* Also we need to make sure that fsMountPath is a substring of exportMountPath if it were to be a sub-directory.
*/
private boolean checkIfSubDirectory(String fsMountPath, String exportMountPath) {
if (exportMountPath.contains(fsMountPath) && !exportMountPath.equals(fsMountPath)) {
return true;
} else {
return false;
}
}
private Map<String, List<String>> sortHostsFromCurrentExports(
List<FileExport> curExpList) {
Map<String, List<String>> currentHostsList = new HashMap<String, List<String>>();
for (FileExport curExport : curExpList) {
if ((null != curExport.getClients())
&& !curExport.getClients().isEmpty()) {
if (curExport.getPermissions().toString().equals(ROOT_PERM)) {
currentHostsList.put(ROOT_HOSTS, curExport.getClients());
} else if (curExport.getPermissions().toString()
.equals(RW_PERM)) {
currentHostsList.put(RW_HOSTS, curExport.getClients());
} else if (curExport.getPermissions().toString()
.equals(RO_PERM)) {
currentHostsList.put(RO_HOSTS, curExport.getClients());
}
}
}
return currentHostsList;
}
private void sortNewEndPoints(List<String> rootHosts, List<String> rwHosts,
List<String> roHosts, List<String> endPointList, String permission) {
for (String endPoint : endPointList) {
if ((null != endPointList) && !endPointList.isEmpty()) {
if (permission.equals(ROOT_PERM)
&& !(rootHosts.contains(endPoint))) {
rootHosts.add(endPoint);
} else if (permission.equals(RW_PERM)
&& !(rwHosts.contains(endPoint))) {
rwHosts.add(endPoint);
} else if (permission.equals(RO_PERM)
&& !(roHosts.contains(endPoint))) {
roHosts.add(endPoint);
}
}
}
}
@Override
public BiosCommandResult doUnexport(StorageSystem storage,
FileDeviceInputOutput args, List<FileExport> exportList)
throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doUnexport: {} - start", args.getFileObjId());
for (int expCount = 0; expCount < exportList.size(); expCount++) {
FileExport export = exportList.get(expCount);
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
if (export.getPermissions() == null) {
export.setPermissions("ro");
}
String mountPath = export.getMountPath();
String exportPath = export.getPath();
if (!nApi.unexportFS(exportPath, mountPath)) {
_log.error("NetAppFileStorageDevice::doUnexport {} failed", args.getFileObjId());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUnexportFileSystem();
serviceError.setMessage(genDetailedMessage("doUnexport", args.getFileObjId().toString()));
result = BiosCommandResult.createErrorResult(serviceError);
return result;
} else {
_log.info("NetAppFileStorageDevice doUnexport {} - completed", args.getFileObjId());
result = BiosCommandResult.createSuccessfulResult();
}
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doUnexport failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUnexportFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doUnexport failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUnexportFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
_log.info("NetAppFileStorageDevice doUnexport {} - complete", args.getFileObjId());
return result;
}
@Override
public BiosCommandResult doExpandFS(StorageSystem storage, FileDeviceInputOutput args)
throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doExpandFS - start");
long newFsExpandSize = args.getNewFSCapacity();
String volumeName = args.getFsName();
if (args.getNewFSCapacity() % BYTESPERMB == 0) {
newFsExpandSize = newFsExpandSize / BYTESPERMB;
} else {
newFsExpandSize = newFsExpandSize / BYTESPERMB + 1;
}
_log.info("FileSystem new size translation : {} : {}", args.getNewFSCapacity(), newFsExpandSize);
String strNewFsSize = String.valueOf(newFsExpandSize) + "m";
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
if (!nApi.setVolumeSize(volumeName, strNewFsSize)) {
_log.error("NetAppFileStorageDevice doExpandFS - failed");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExpandFileSystem();
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("NetAppFileStorageDevice doExpandFS - complete");
result = BiosCommandResult.createSuccessfulResult();
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doExpandFS failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExpandFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doExpandFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToExpandFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
/**
* Creates FileShare CIFS/SMB shares
*
* @param StorageSystem storage
* @param SMBFileShare smbFileShare
* @return BiosCommandResult
* @throws ControllerException
*/
@Override
public BiosCommandResult doShare(StorageSystem storage,
FileDeviceInputOutput args, SMBFileShare smbFileShare)
throws ControllerException {
// To be in-sync with isilon implementation, currently forceGroup is
// set to null which will set the group name as "everyone" by default.
String forceGroup = null;
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doShare - start");
SMBShareMap smbShareMap = args.getFileObjShares();
SMBFileShare existingShare = (smbShareMap == null) ? null
: smbShareMap.get(smbFileShare.getName());
Boolean modOrCreateShareSuccess;
if (existingShare != null) {
modOrCreateShareSuccess = modifyNtpShare(storage, args, smbFileShare, forceGroup, existingShare);
} else {
modOrCreateShareSuccess = createNtpShare(storage, args, smbFileShare, forceGroup);
}
if (modOrCreateShareSuccess.booleanValue() == true) {
_log.info("NetAppFileStorageDevice doShare {} - complete",
smbFileShare.getName());
// Update the collection.
if (args.getFileObjShares() == null) {
args.initFileObjShares();
}
// set Mount Point
smbFileShare.setMountPoint(smbFileShare.getNetBIOSName(), smbFileShare.getStoragePortNetworkId(),
smbFileShare.getStoragePortName(), smbFileShare.getName());
args.getFileObjShares().put(smbFileShare.getName(), smbFileShare);
result = BiosCommandResult.createSuccessfulResult();
} else {
_log.error("NetAppFileStorageDevice doShare {} - failed",
smbFileShare.getName());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileShare();
result = BiosCommandResult.createErrorResult(serviceError);
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doShare failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileShare();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doShare failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateFileShare();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
/**
* Deletes CIFS FileShare
*
* @param StorageSystem storage
* @param FileDeviceInputOutput args
* @param SMBFileShare smbFileShare
* @return BiosCommandResult
* @throws ControllerException
*/
@Override
public BiosCommandResult doDeleteShare(StorageSystem storage,
FileDeviceInputOutput args, SMBFileShare smbFileShare)
throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doDeleteShare - start");
FileShare fileshare = null;
if (args.getFileOperation() == true) {
fileshare = args.getFs();
} else {
URI snapShotUID = args.getSnapshotId();
Snapshot snapshot = _dbClient.queryObject(Snapshot.class, snapShotUID);
fileshare = _dbClient.queryObject(FileShare.class, snapshot.getParent().getURI());
}
// Now get the VFiler from the fileShare
String portGroup = findVfilerName(fileshare);
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
SMBShareMap shares = args.getFileObjShares();
if (shares == null || shares.isEmpty()) {
_log.error("NetAppFileStorageDevice::doDeleteShare failed: FileShare(s) is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileShare();
serviceError.setMessage("FileShare(s) is either missing or empty");
result = BiosCommandResult.createErrorResult(serviceError);
}
SMBFileShare fileShare = shares.get(smbFileShare.getName());
if (fileShare != null) {
if (!nApi.deleteShare(smbFileShare.getName())) {
_log.error("NetAppFileStorageDevice doDeleteShare {} - failed",
args.getFileObjId());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileShare();
serviceError.setMessage("Deletion of CIFS File Share failed");
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("NetAppFileStorageDevice doDeleteShare {} - complete",
args.getFileObjId());
args.getFileObjShares().remove(smbFileShare.getName());
args.getFileObjShares().remove(smbFileShare.getNativeId());
result = BiosCommandResult.createSuccessfulResult();
}
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doDeleteShare failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileShare();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doCreateFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteFileShare();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
/**
* Deletes CIFS FileShares
*
* @param StorageSystem storage
* @param FileDeviceInputOutput args
* @return BiosCommandResult
* @throws ControllerException
*/
@Override
public BiosCommandResult doDeleteShares(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
result.setCommandSuccess(false);
result.setCommandStatus(Operation.Status.error.name());
result.setMessage("Delete shares for multiple SMB is not supported.");
return result;
}
@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 NetApp.");
return result;
}
@Override
public BiosCommandResult doSnapshotFS(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doSnapshotFS - start");
if (null == args.getFsName()) {
_log.error("NetAppFileStorageDevice::doSnapshotFS failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateSnapshot();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == args.getSnapshotName()) {
_log.error("NetAppFileStorageDevice::doSnapshotFS failed: Snapshot name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateSnapshot();
serviceError.setMessage(FileSystemConstants.FS_ERR_SNAPSHOT_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
boolean failedStatus = false;
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(), storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
if (!nApi.createSnapshot(args.getFsName(), args.getSnapshotName())) {
failedStatus = true;
}
if (failedStatus == true) {
_log.error("NetAppFileStorageDevice doSnapshotFS {} - failed", args.getFsId());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateSnapshot();
serviceError.setMessage(genDetailedMessage("doSnapshotFS", args.getFsName()));
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("doSnapshotFS - Snapshot, {} was successfully created for filesystem, {} ", args.getSnapshotName(),
args.getFsName());
// Set snapshot Path and MountPath information
args.setSnapshotMountPath(getSnapshotMountPath(args.getFsMountPath(), args.getSnapshotName()));
args.setSnapshotPath(getSnapshotPath(args.getFsPath(), args.getSnapshotName()));
args.setSnapNativeId(args.getSnapshotName());
result = BiosCommandResult.createSuccessfulResult();
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doSnapshotFS failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateSnapshot();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doSnapshotFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateSnapshot();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
@Override
public BiosCommandResult doRestoreFS(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doRestoreFS - start");
if (null == args.getFsName()) {
_log.error("NetAppFileStorageDevice::doRestoreFS failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToRestoreFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == args.getSnapshotName()) {
_log.error("NetAppFileStorageDevice::doRestoreFS failed: Snapshot name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToRestoreFileSystem();
serviceError.setMessage(FileSystemConstants.FS_ERR_SNAPSHOT_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(), storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
if (!nApi.restoreSnapshot(args.getFsName(), args.getSnapshotName())) {
_log.error("NetAppFileStorageDevice doRestoreFS {} - failed", args.getFsName());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToRestoreFileSystem();
result = BiosCommandResult.createErrorResult(serviceError);
} else {
_log.info("doRestoreFS - restore of snapshot, {} was successfully for filesystem, {} ", args.getSnapshotName(),
args.getFsName());
result = BiosCommandResult.createSuccessfulResult();
}
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doRestoreFS failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToRestoreFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doRestoreFS failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToRestoreFileSystem();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
// Get FS snapshot list from the array.
@Override
public BiosCommandResult getFSSnapshotList(StorageSystem storage,
FileDeviceInputOutput args, List<String> dbSnapshots) throws ControllerException {
if (null == args.getFsName()) {
throw new DeviceControllerException(
"Filesystem name is either missing or empty",
new Object[] {});
}
_log.info("NetAppFileStorageDevice getFSSnapshotList: {} - start",
args.getFsName());
BiosCommandResult result = new BiosCommandResult();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
try {
List<String> deviceSnapshots = nApi.listSnapshots(args.getFsName());
if (deviceSnapshots == null) {
_log.warn("NetAppFileStorageDevice getFSSnapshotList {} - failed",
args.getFsId());
result.setCommandSuccess(false);
result.setMessage("NetAppFileStorageDevice getFSSnapshotList failed for FS {}"
+ args.getFsName());
result.setCommandStatus(Operation.Status.error.name());
} else {
for (String deviceSnapshotName : deviceSnapshots) {
dbSnapshots.add(deviceSnapshotName);
}
_log.info(
"NetAppFileStorageDevice getFSSnapshotList - successful for filesystem, {} ",
args.getFsName());
result.setCommandSuccess(true);
result.setCommandStatus(Operation.Status.ready.name());
result.setMessage("List of snapshots for FS " + args.getFsName()
+ " was successfully retreived from device ");
}
} catch (NetAppException e) {
String[] params = { storage.getId().toString(), args.getFsName() };
throw new DeviceControllerException(
"Failed to retrieve list of snapshots from device {1} for filesystem {2}",
params);
}
return result;
}
@Override
public BiosCommandResult doDeleteSnapshot(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
if (null == args.getFsName()) {
throw new DeviceControllerException(
"Filesystem name is either missing or empty",
new Object[] {});
}
if (null == args.getSnapshotName()) {
throw new DeviceControllerException(
"Snapshot name is either missing or empty for filesystem name {0}",
new Object[] { args.getFsName() });
}
_log.info("NetAppFileStorageDevice doDeleteSnapshot: {},{} - start",
args.getSnapshotId(), args.getSnapshotName());
BiosCommandResult result = new BiosCommandResult();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
try {
if (!nApi.deleteSnapshot(args.getFsName(), args.getSnapshotName())) {
_log.error(
"NetAppFileStorageDevice doDeleteSnapshot {} - failed",
args.getFsId());
result.setCommandSuccess(false);
result.setMessage("NetAppFileStorageDevice doDeleteSnapshot - failed");
result.setCommandStatus(Operation.Status.error.name());
} else {
_log.info(
"doDeleteSnapshot for snapshot {} on filesystem {} successful",
args.getSnapshotName(), args.getFsName());
result.setCommandSuccess(true);
result.setCommandStatus(Operation.Status.ready.name());
result.setMessage("Snapshot," + args.getSnapshotName()
+ " has been successfully deleted");
}
} catch (NetAppException e) {
throw new DeviceControllerException(
"Failed to delete snapshot, {0} for filesystem, {1} with: {2}",
new Object[] { args.getSnapshotName(), args.getFsName(),
e.getMessage() });
}
return result;
}
@Override
public void doConnect(StorageSystem storage) {
// FIX ME
}
@Override
public void doDisconnect(StorageSystem storage) {
// FIX ME
}
@Override
public BiosCommandResult getPhysicalInventory(StorageSystem storage) {
BiosCommandResult result = new BiosCommandResult();
result.setCommandSuccess(true);
return result;
}
// Validate the export arguments for the VNX.
//
// The root user mapping must be an integer or the value 'nobody'. Since
// 'nobody' is the default value on the NetApp, it does not need to be sent.
// The CLI only accepts UIDs (integer values).
private void validateExportArgs(List<FileExport> exports)
throws ControllerException {
String rootUser = "";
for (FileExport exp : exports) {
// Validate the root user mapping (specific to VNX)
_log.info("FileExport:Clients:" + exp.getClients() + ":SPName:"
+ exp.getStoragePortName() + ":SP:" + exp.getStoragePort()
+ ":rootusermapping:" + exp.getRootUserMapping() + ":perm:"
+ exp.getPermissions() + ":protocol:" + exp.getProtocol()
+ ":security:" + exp.getSecurityType() + ":subDir:"
+ exp.getPath());
rootUser = exp.getRootUserMapping();
try {
if (!rootUser.equalsIgnoreCase("nobody")
&& !rootUser.equalsIgnoreCase("root")) {
Integer.parseInt(rootUser);
}
} catch (NumberFormatException nfe) {
throw new DeviceControllerException(
"Invalid Root User Mapping {0} ",
new Object[] { nfe });
}
}
}
/**
* create NetApp snapshot path from file share path and snapshot name
*
* @param fsPath mount path of the fileshare
* @param name snapshot name
* @return String
*/
private String getSnapshotPath(String fsPath, String name) {
return String.format("%1$s/.snapshot/%2$s",
fsPath, name);
}
/**
* create NetApp snapshot path from file share path and snapshot name
*
* @param fsPath mount path of the fileshare
* @param name snapshot name
* @return String
*/
private String getSnapshotMountPath(String fsPath, String name) {
return String.format("%1$s/.snapshot/%2$s",
fsPath, name);
}
/**
* create NetApp share with right permissions
*
* @param StorageSystem mount path of the fileshare
* @param args containing input/out arguments of filedevice
* @param smbFileShare smbFileshare object
* @param forceGroup Name of the group the fileshare belongs.
* @return
*/
private Boolean createNtpShare(StorageSystem storage,
FileDeviceInputOutput args, SMBFileShare smbFileShare,
String forceGroup) throws NetAppException {
String shareId = null;
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
shareId = smbFileShare.getPath();
_log.info("NetAppFileStorageDevice doShare for {} with id {}", shareId, args.getFileObjId());
if (!nApi.doShare(shareId, smbFileShare.getName(),
smbFileShare.getDescription(), smbFileShare.getMaxUsers(),
smbFileShare.getPermission(), forceGroup)) {
_log.info("NetAppFileStorageDevice doShare for {} with id {} - failed",
shareId, args.getFileObjId());
return false;
} else {
// share creation is successful,no need to set permission,clear the default one.
List<CifsAcl> existingAcls = new ArrayList<CifsAcl>();
CifsAcl defaultAcl = new CifsAcl();
// By default NetApp share get everyone full access.
defaultAcl.setUserName("everyone");
defaultAcl.setAccess(CifsAccess.full);
existingAcls.add(defaultAcl);
nApi.deleteCIFSShareAcl(smbFileShare.getName(), existingAcls);
smbFileShare.setNativeId(shareId);
if (null != args.getFileObj()) {
nApi.setQtreemode(args.getFsPath(), NTFS_QTREE_SETTING);
}
smbFileShare.setNetBIOSName(nApi.getNetBiosName());
_log.info("NetAppFileStorageDevice doShare for {} with id {} - complete",
shareId, args.getFileObjId());
return true;
}
}
/**
* modify NetApp share with right permissions and other parameters
*
* @param StorageSystem mount path of the fileshare
* @param args containing input/out arguments of filedevice
* @param smbFileShare existingShare smbFileshare object that needs to be modified.
* @param forceGroup Name of the group the fileshare belongs.
* @return
*/
private Boolean modifyNtpShare(StorageSystem storage,
FileDeviceInputOutput args, SMBFileShare smbFileShare,
String forceGroup, SMBFileShare existingShare) throws NetAppException {
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
// String shareId = args.getFileObj().getPath();
String shareId = smbFileShare.getPath();
if (!nApi.modifyShare(shareId, smbFileShare.getName(), smbFileShare.getDescription(),
smbFileShare.getMaxUsers(), smbFileShare.getPermission(), forceGroup)) {
_log.info(
"NetAppFileStorageDevice doShare (modification) for {} with id {} - failed",
shareId, args.getFileObjId());
return false;
} else {
_log.info(
"NetAppFileStorageDevice doShare (modification) for {} with id {} - complete",
shareId, args.getFileObjId());
return true;
}
}
/**
* Return the vFiler name associated with the file system. If a vFiler is not associated with
* this file system, then it will return null.
*/
private String findVfilerName(FileShare fs) {
String portGroup = null;
URI port = fs.getStoragePort();
if (port == null) {
_log.info("No storage port URI to retrieve vFiler name");
} else {
StoragePort stPort = _dbClient.queryObject(StoragePort.class, port);
if (stPort != null) {
URI haDomainUri = stPort.getStorageHADomain();
if (haDomainUri == null) {
_log.info("No Port Group URI for port {}", port);
} else {
StorageHADomain haDomain = _dbClient.queryObject(StorageHADomain.class, haDomainUri);
if (haDomain != null && haDomain.getVirtual() == true) {
portGroup = stPort.getPortGroup();
_log.debug("using port {} and vFiler {}", stPort.getPortNetworkId(), portGroup);
}
}
}
}
return portGroup;
}
/**
* Return true if qtree name and its path in valid length, otherwise false.
*/
private Boolean validQuotaDirectoryPath(String volName, String quotaDirName) {
if (volName == null && quotaDirName == null) {
_log.info("Invalid volume name and quota directory name ");
return false;
} else {
// NetApp accepts maximum of 64-character as a quota directory name.
if (quotaDirName.length() > QUOTA_DIR_MAX_NAME) {
_log.error("quota directory name is too long {}, maximum {} chars", quotaDirName.length(), QUOTA_DIR_MAX_NAME);
return false;
}
String qtreePath = VOL_ROOT + volName + "/" + quotaDirName;
// each entry in /etc/quotas file is with maximum of 160 characters.
// "/vol/<volname>/qtreename tree <size> - - - -"
if (qtreePath.length() > QUOTA_DIR_MAX_PATH) {
_log.error("quota directory path is too long {}, maximum {} chars", qtreePath.length(), QUOTA_DIR_MAX_PATH);
return false;
}
}
return true;
}
// New Qtree methods
@Override
public BiosCommandResult doCreateQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args, QuotaDirectory qtree) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doCreateQuotaDirectory - start");
String volName = args.getFsName(); // Using NetApp terminology here
String qtreeName = args.getQuotaDirectoryName();
Boolean oplocks = qtree.getOpLock();
String securityStyle = qtree.getSecurityStyle();
Long size = qtree.getSize();
if (null == volName) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == qtreeName) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed: Qtree name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_QUOTADIR_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (!validQuotaDirectoryPath(volName, qtreeName)) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed: Qtree name or path is too long");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_QUOTADIR_NAME_PATH_TOO_LONG);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
String portGroup = findVfilerName(args.getFs());
_log.info("NetAppFileStorageDevice::NetAppFileStorageDevice - For FS: {}, vFiler: {}", volName, portGroup);
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
nApi.createQtree(qtreeName, volName, oplocks, securityStyle, size, portGroup);
result = BiosCommandResult.createSuccessfulResult();
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed with a NetAppException", e);
_log.info("NetAppFileStorageDevice::doCreateQuotaDirectory e.getLocalizedMessage(): {}", e.getLocalizedMessage());
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
@Override
public BiosCommandResult doDeleteQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doDeleteQuotaDirectory - start");
String volName = args.getFsName(); // Using NetApp terminology here
String qtreeName = args.getQuotaDirectoryName();
if (null == volName) {
_log.error("NetAppFileStorageDevice::doDeleteQuotaDirectory failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == qtreeName) {
_log.error("NetAppFileStorageDevice::doCreateQuotaDirectory failed: Qtree name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_QUOTADIR_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
nApi.deleteQtree(qtreeName, volName, portGroup);
result = BiosCommandResult.createSuccessfulResult();
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doDeleteQuotaDirectory failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doDeleteQuotaDirectory failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
@Override
public BiosCommandResult doUpdateQuotaDirectory(StorageSystem storage,
FileDeviceInputOutput args, QuotaDirectory qtree) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
try {
_log.info("NetAppFileStorageDevice doUpdateQuotaDirectory - start");
String volName = args.getFsName(); // Using NetApp terminology here
String qtreeName = args.getQuotaDirectoryName();
Boolean oplocks = qtree.getOpLock();
String securityStyle = qtree.getSecurityStyle();
Long size = qtree.getSize();
if (null == volName) {
_log.error("NetAppFileStorageDevice::doUpdateQuotaDirectory failed: Filesystem name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUpdateQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_FS_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
if (null == qtreeName) {
_log.error("NetAppFileStorageDevice::doUpdateQuotaDirectory failed: Qtree name is either missing or empty");
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUpdateQtree();
serviceError.setMessage(FileSystemConstants.FS_ERR_QUOTADIR_NAME_MISSING_OR_EMPTY);
result = BiosCommandResult.createErrorResult(serviceError);
return result;
}
String portGroup = findVfilerName(args.getFs());
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
nApi.updateQtree(qtreeName, volName, oplocks, securityStyle, size, portGroup);
result = BiosCommandResult.createSuccessfulResult();
} catch (NetAppException e) {
_log.error("NetAppFileStorageDevice::doUpdateQuotaDirectory failed with a NetAppException", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::doUpdateQuotaDirectory failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToCreateQtree();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
@Override
public BiosCommandResult deleteExportRules(StorageSystem storage,
FileDeviceInputOutput args) throws ControllerException {
BiosCommandResult result = new BiosCommandResult();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
List<ExportRule> allExports = args.getExistingDBExportRules();
String subDir = args.getSubDirectory();
boolean allDirs = args.isAllDir();
FileShare fs = args.getFs();
String exportPath;
String subDirExportPath = "";
subDir = args.getSubDirectory();
if (!args.getFileOperation()) {
exportPath = args.getSnapshotPath();
if (subDir != null
&& subDir.length() > 0) {
subDirExportPath = args.getSnapshotPath() + "/"
+ subDir;
}
} else {
exportPath = args.getFs().getPath();
if (subDir != null
&& subDir.length() > 0) {
subDirExportPath = args.getFs().getPath() + "/"
+ subDir;
}
}
_log.info("exportPath : {}", exportPath);
args.setExportPath(exportPath);
_log.info("Number of existing exports found {}", allExports.size());
try {
if (allDirs) {
Set<String> allPaths = new HashSet<String>();
// ALL EXPORTS
_log.info("Deleting all exports specific to filesystem at device and rules from DB including sub dirs rules and exports");
for (ExportRule rule : allExports) {
allPaths.add(rule.getExportPath());
}
for (String path : allPaths) {
_log.info("deleting export path : {} ", path);
nApi.deleteNFSExport(path);
}
} else if (subDir != null && !subDir.isEmpty()) {
// Filter for a specific Sub Directory export
_log.info("Deleting all subdir exports rules at ViPR and sub directory export at device {}", subDir);
for (ExportRule rule : allExports) {
if (rule.getExportPath().endsWith("/" + subDir)) {
nApi.deleteNFSExport(subDirExportPath);
break;
}
}
} else {
// Filter for No SUBDIR - main export rules with no sub dirs
_log.info("Deleting all export rules from DB and export at device not included sub dirs");
nApi.deleteNFSExport(exportPath);
}
} catch (NetAppException e) {
_log.info("Exception:" + e.getMessage());
throw new DeviceControllerException(
"Exception while performing export for {0} ",
new Object[] { args.getFsId() });
}
_log.info("NetAppFileStorageDevice exportFS {} - complete",
args.getFsId());
result.setCommandSuccess(true);
result.setCommandStatus(Operation.Status.ready.name());
return result;
}
@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();
// To be processed export rules
List<ExportRule> exportsToRemove = new ArrayList<>();
List<ExportRule> exportsToAdd = new ArrayList<>();
String exportPath;
String subDir = args.getSubDirectory();
if (!args.getFileOperation()) {
exportPath = args.getSnapshotPath();
if (subDir != null && subDir.length() > 0) {
exportPath = args.getSnapshotPath() + "/" + subDir;
}
} else {
exportPath = args.getFs().getPath();
if (subDir != null && subDir.length() > 0) {
exportPath = args.getFs().getPath() + "/" + subDir;
}
}
_log.info("exportPath : {}", exportPath);
args.setExportPath(exportPath);
// ALL EXPORTS
List<ExportRule> existingDBExportRule = args.getExistingDBExportRules();
List<ExportRule> exportsToprocess = new ArrayList<>();
for (ExportRule rule : existingDBExportRule) {
if (rule.getExportPath().equalsIgnoreCase(exportPath)) {
exportsToprocess.add(rule);
}
}
_log.info("Number of existng Rules found {}", exportsToprocess.size());
// Handle Modified export Rules
if (exportsToprocess != null && !exportsToprocess.isEmpty()) {
for (ExportRule existingRule : exportsToprocess) {
for (ExportRule modifiedrule : exportModify) {
if (modifiedrule.getSecFlavor().equals(
existingRule.getSecFlavor())) {
_log.info("Modifying Export Rule from {}, To {}",
existingRule, modifiedrule);
// use a separate list to avoid concurrent modification exception for now.
exportsToRemove.add(existingRule);
exportsToAdd.add(modifiedrule);
}
}
}
// Handle Add export Rules
if (exportAdd != null && !exportAdd.isEmpty()) {
for (ExportRule newExport : exportAdd) {
_log.info("Adding Export Rule {}", newExport);
exportsToAdd.add(newExport);
}
}
// Handle Delete export Rules
if (exportDelete != null && !exportDelete.isEmpty()) {
for (ExportRule existingRule : exportsToprocess) {
for (ExportRule oldExport : exportDelete) {
if (oldExport.getSecFlavor().equals(
existingRule.getSecFlavor())) {
_log.info("Deleting Export Rule {}", existingRule);
exportsToRemove.add(existingRule);
}
}
}
}
// No of exports found to remove from the list
_log.info("No of exports found to remove from the existing exports list {}", exportsToRemove.size());
exportsToprocess.removeAll(exportsToRemove);
_log.info("No of exports found to add to the existing exports list {}", exportsToAdd.size());
exportsToprocess.addAll(exportsToAdd);
// Since NetApp will remove the export itself when we removed all the export rules,
// adding a default rule will keep the export alive.
if (exportsToprocess.isEmpty() && !exportsToRemove.isEmpty()) {
// If all exports rules deleted, export will get deleted too. So set back to its defaults
ExportRule rule = new ExportRule();
rule.setSecFlavor("sys");
rule.setAnon("root");
java.util.Set<String> roHosts = new HashSet<>();
roHosts.add("Default");
rule.setReadOnlyHosts(roHosts);
exportsToprocess.add(rule);
}
} else {
if (exportsToprocess == null) {
exportsToprocess = new ArrayList<>();
}
exportsToprocess.addAll(exportAdd);
exportsToprocess.addAll(exportModify);
}
// if only delete provided with no existing rules -- How do we handle this? [GOPI]
_log.info("Number of Export Rules to update after processing found {}", exportsToprocess.size());
BiosCommandResult result = new BiosCommandResult();
try {
String portGroup = null;
if (args.getFileOperation() == true) {
FileShare fileshare = args.getFs();
portGroup = findVfilerName(fileshare);
} else {
// Get the FS from the snapshot
URI snapshotUID = args.getSnapshotId();
Snapshot snapshot = _dbClient.queryObject(Snapshot.class, snapshotUID);
FileShare fileshare = _dbClient.queryObject(FileShare.class, snapshot.getParent().getURI());
portGroup = findVfilerName(fileshare);
}
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).vFiler(portGroup).build();
if (!nApi
.modifyNFSShare(exportPath, exportsToprocess)) {
_log.error("NetAppFileStorageDevice updateFSExportRules {} - failed",
args.getFsId());
result.setMessage("NetAppFileStorageDevice updateFSExportRules {} - failed");
result.setCommandStatus(Operation.Status.error.name());
return result;
}
if ((args.getFileOperation() == true)
&& args.getSubDirectory() == null) {
nApi.setQtreemode(exportPath, UNIX_QTREE_SETTING);
}
} catch (NetAppException e) {
_log.info("Exception:" + e.getMessage());
throw new DeviceControllerException(
"Exception while performing export for {0} ",
new Object[] { args.getFsId() });
}
_log.info("NetAppFileStorageDevice updateFSExportRules {} - complete",
args.getFsId());
result.setCommandSuccess(true);
result.setCommandStatus(Operation.Status.ready.name());
return result;
}
@Override
public BiosCommandResult updateShareACLs(StorageSystem storage,
FileDeviceInputOutput args) {
List<ShareACL> existingAcls = new ArrayList<ShareACL>();
existingAcls = args.getExistingShareAcls();
BiosCommandResult result = new BiosCommandResult();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
try {
processAclsForShare(nApi, args);
result = BiosCommandResult.createSuccessfulResult();
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::updateShareACLs failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUpdateCIFSShareAcl();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
// if delete or modify fails , revert to old acl
_log.info("update ACL failed ,going to roll back to existing ACLs");
rollbackShareACLs(storage, args, existingAcls);
}
return result;
}
private void processAclsForShare(NetAppApi nApi,
FileDeviceInputOutput args) {
// add and modify are same for NetApp call.
List<ShareACL> aclsToAddModify = new ArrayList<ShareACL>();
aclsToAddModify.addAll(args.getShareAclsToAdd());
aclsToAddModify.addAll(args.getShareAclsToModify());
List<ShareACL> aclsToDelete = args.getShareAclsToDelete();
String shareName = args.getShareName();
deleteShareAcl(nApi, shareName, aclsToDelete);
addShareAcl(nApi, shareName, aclsToAddModify);
}
private void addShareAcl(NetAppApi nApi, String shareName, List<ShareACL> aclsToAdd) {
if (aclsToAdd == null || aclsToAdd.isEmpty()) {
return;
}
List<CifsAcl> acls = new ArrayList<CifsAcl>();
for (ShareACL newAcl : aclsToAdd) {
CifsAcl cif_new = new CifsAcl();
String domain = newAcl.getDomain();
String userOrGroup = newAcl.getGroup() == null ? newAcl.getUser() : newAcl.getGroup();
if (domain != null && !domain.isEmpty()) {
userOrGroup = domain + "\\" + userOrGroup;
}
// for netapp api user and group are same.and need to set only user
cif_new.setUserName(userOrGroup);
cif_new.setShareName(shareName);
cif_new.setAccess(getAccessEnum(newAcl.getPermission()));
acls.add(cif_new);
}
nApi.modifyCIFSShareAcl(shareName, acls);
}
private CifsAccess getAccessEnum(String permission) throws NetAppException {
CifsAccess access = null;
if (permission != null) {
switch (permission.toLowerCase()) {
case "read":
access = CifsAccess.read;
break;
case "change":
access = CifsAccess.change;
break;
case "fullcontrol":
access = CifsAccess.full;
break;
default:
throw new IllegalArgumentException(permission + " is not a valid permission for Cifs Share");
}
}
return access;
}
private void deleteShareAcl(NetAppApi nApi, String shareName, List<ShareACL> aclsToDelete) {
if (aclsToDelete == null || aclsToDelete.isEmpty()) {
return;
}
List<CifsAcl> acls = new ArrayList<CifsAcl>();
for (ShareACL newAcl : aclsToDelete) {
CifsAcl cif_new = new CifsAcl();
String domain = newAcl.getDomain();
String userOrGroup = newAcl.getGroup() == null ? newAcl.getUser() : newAcl.getGroup();
if (domain != null && !domain.isEmpty()) {
userOrGroup = domain + "\\" + userOrGroup;
}
// for netapp api user and group are same.and need to set only user
cif_new.setUserName(userOrGroup);
cif_new.setShareName(shareName);
cif_new.setAccess(getAccessEnum(newAcl.getPermission()));
acls.add(cif_new);
}
nApi.deleteCIFSShareAcl(shareName, acls);
}
private BiosCommandResult rollbackShareACLs(StorageSystem storage, FileDeviceInputOutput args,
List<ShareACL> existingList) {
BiosCommandResult result = new BiosCommandResult();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
try {
// We can have multiple ace added/modified in one put call ,some of them can fail due to some reason.
// In case of failure, to make it consistent in vipr db and NetApp share, delete all currently
// added and modified ace and revert it to old acl.
_log.info("NetAppFileStorageDevice::Rolling back update ACL by trying delete ACL for share {}", args.getShareName());
List<ShareACL> aclsToClear = new ArrayList<ShareACL>();
aclsToClear.addAll(args.getShareAclsToAdd());
aclsToClear.addAll(args.getShareAclsToModify());
forceDeleteShareAcl(nApi, args.getShareName(), aclsToClear);
_log.info("NetAppFileStorageDevice::Adding back old ACL to Share {}", args.getShareName());
forceAddShareAcl(nApi, args.getShareName(), existingList);
result = BiosCommandResult.createSuccessfulResult();
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::Roll Back of ACL failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToUpdateCIFSShareAcl();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
private void forceDeleteShareAcl(NetAppApi nApi, String shareName, List<ShareACL> aclsToDelete) {
if (aclsToDelete == null || aclsToDelete.isEmpty()) {
return;
}
List<CifsAcl> acls = new ArrayList<CifsAcl>();
for (ShareACL newAcl : aclsToDelete) {
CifsAcl cif_new = new CifsAcl();
String domain = newAcl.getDomain();
String userOrGroup = newAcl.getGroup() == null ? newAcl.getUser() : newAcl.getGroup();
if (domain != null && !domain.isEmpty()) {
userOrGroup = domain + "\\" + userOrGroup;
}
// for netapp api user and group are same.and need to set only user
cif_new.setUserName(userOrGroup);
cif_new.setShareName(shareName);
cif_new.setAccess(getAccessEnum(newAcl.getPermission()));
acls.add(cif_new);
}
for (CifsAcl cifsAcl : acls) {
try {
List<CifsAcl> singleACL = new ArrayList<CifsAcl>();
singleACL.add(cifsAcl);
nApi.deleteCIFSShareAcl(shareName, singleACL);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice:: Force delete of ACL for user [" + cifsAcl.getUserName()
+ "] failed with an Exception", e);
}
}
}
private void forceAddShareAcl(NetAppApi nApi, String shareName, List<ShareACL> aclsToAdd) {
if (aclsToAdd == null || aclsToAdd.isEmpty()) {
return;
}
List<CifsAcl> acls = new ArrayList<CifsAcl>();
for (ShareACL newAcl : aclsToAdd) {
CifsAcl cif_new = new CifsAcl();
String domain = newAcl.getDomain();
String userOrGroup = newAcl.getGroup() == null ? newAcl.getUser() : newAcl.getGroup();
if (domain != null && !domain.isEmpty()) {
userOrGroup = domain + "\\" + userOrGroup;
}
// for netapp api user and group are same.and need to set only user
cif_new.setUserName(userOrGroup);
cif_new.setShareName(shareName);
cif_new.setAccess(getAccessEnum(newAcl.getPermission()));
acls.add(cif_new);
}
for (CifsAcl cifsAcl : acls) {
try {
List<CifsAcl> singleACL = new ArrayList<CifsAcl>();
singleACL.add(cifsAcl);
nApi.modifyCIFSShareAcl(shareName, singleACL);
} catch (Exception e) {
_log.error("NetAppFileStorageDevice:: Force add of ACL for user [" + cifsAcl.getUserName() + "] failed with an Exception",
e);
}
}
}
@Override
public BiosCommandResult deleteShareACLs(StorageSystem storage,
FileDeviceInputOutput args) {
BiosCommandResult result = new BiosCommandResult();
List<ShareACL> existingAcls = new ArrayList<ShareACL>();
existingAcls = args.getExistingShareAcls();
NetAppApi nApi = new NetAppApi.Builder(storage.getIpAddress(),
storage.getPortNumber(), storage.getUsername(),
storage.getPassword()).https(true).build();
try {
deleteShareAcl(nApi, args.getShareName(), existingAcls);
result = BiosCommandResult.createSuccessfulResult();
} catch (Exception e) {
_log.error("NetAppFileStorageDevice::Delete All ACL failed with an Exception", e);
ServiceError serviceError = DeviceControllerErrors.netapp.unableToDeleteCIFSShareAcl();
serviceError.setMessage(e.getLocalizedMessage());
result = BiosCommandResult.createErrorResult(serviceError);
}
return result;
}
@Override
public BiosCommandResult updateNfsACLs(StorageSystem storage, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult deleteNfsACLs(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult assignFilePolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult unassignFilePolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult listSanpshotByPolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult updateStorageSystemFileProtectionPolicy(StorageSystem storageObj, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
@Override
public BiosCommandResult checkFilePolicyPathHasResourceLabel(StorageSystem system, FileDeviceInputOutput args) {
return BiosCommandResult.createErrorResult(
DeviceControllerErrors.netapp.operationNotSupported());
}
}