/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.vnxe.job;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
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.URIUtil;
import com.emc.storageos.db.client.constraint.AlternateIdConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.FSExportMap;
import com.emc.storageos.db.client.model.FileExport;
import com.emc.storageos.db.client.model.FileExportRule;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.Snapshot;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.model.file.ExportRule;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.util.ExportUtils;
import com.emc.storageos.vnxe.VNXeApiClient;
import com.emc.storageos.vnxe.models.VNXeBase;
import com.emc.storageos.vnxe.models.VNXeNfsShare;
import com.emc.storageos.volumecontroller.FileShareExport;
import com.emc.storageos.volumecontroller.JobContext;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.FileDeviceController;
public class VNXeModifyExportJob extends VNXeJob {
private static final long serialVersionUID = 1L;
private static final Logger _logger = LoggerFactory.getLogger(VNXeModifyExportJob.class);
private boolean isFile;
private boolean isDeleteRule;
private ExportRule rule;
private String exportPath;
private FileShareExport exportInfo;
private String shareName;
public VNXeModifyExportJob(String jobId, URI storageSystemUri,
TaskCompleter taskCompleter, ExportRule rule, FileShareExport exportInfo, String exportPath, boolean isFile,
boolean isDeleteRule, String shareName) {
super(jobId, storageSystemUri, taskCompleter, "updateExportRules");
this.isFile = isFile;
this.isDeleteRule = isDeleteRule;
this.rule = rule;
this.exportPath = exportPath;
this.exportInfo = exportInfo;
this.shareName = shareName;
}
/**
* Called to update the job status when the export file system job completes.
*
* @param jobContext The job context.
*/
@Override
public void updateStatus(JobContext jobContext) throws Exception {
DbClient dbClient = jobContext.getDbClient();
try {
if (_status == JobStatus.IN_PROGRESS) {
return;
}
VNXeApiClient vnxeApiClient = getVNXeClient(jobContext);
String opId = getTaskCompleter().getOpId();
StringBuilder logMsgBuilder = new StringBuilder(String.format("Updating status of job %s to %s", opId, _status.name()));
FileShare fsObj = null;
Snapshot snapObj = null;
URI objId = getTaskCompleter().getId();
StorageSystem storageObj = dbClient.queryObject(StorageSystem.class, getStorageSystemUri());
if (_status == JobStatus.SUCCESS) {
_isSuccess = true;
FileExport newExport = null;
if (exportInfo != null) {
newExport = exportInfo.getFileExport();
}
VNXeNfsShare nfsShare = null;
if (isFile) {
fsObj = dbClient.queryObject(FileShare.class, objId);
nfsShare = vnxeApiClient.getNfsShareById(rule.getDeviceExportId());
// nfsShare = vnxeApiClient.findNfsShare(fsObj.getNativeId(), shareName);
updateExportRules(vnxeApiClient, dbClient, fsObj, nfsShare);
if (newExport != null) {
updateFSExport(fsObj, dbClient, newExport);
}
} else {
snapObj = dbClient.queryObject(Snapshot.class, objId);
fsObj = dbClient.queryObject(FileShare.class, snapObj.getParent().getURI());
nfsShare = vnxeApiClient.findSnapNfsShare(snapObj.getNativeId(), shareName);
updateExportRules(vnxeApiClient, dbClient, fsObj, nfsShare);
if (newExport != null) {
updateSnapshotExport(snapObj, dbClient, newExport);
}
}
} else if (_status == JobStatus.FAILED) {
// cleanupFSExport(fsObj, dbClient);
logMsgBuilder.append("\n");
logMsgBuilder.append(String.format(
"Task %s failed to update export rules: %s", opId, objId.toString()));
}
_logger.info(logMsgBuilder.toString());
if (isFile) {
FileDeviceController.recordFileDeviceOperation(dbClient, OperationTypeEnum.UPDATE_EXPORT_RULES_FILE_SYSTEM,
_isSuccess, logMsgBuilder.toString(), "", fsObj, storageObj);
} else {
FileDeviceController.recordFileDeviceOperation(dbClient, OperationTypeEnum.UPDATE_EXPORT_RULES_FILE_SNAPSHOT,
_isSuccess, logMsgBuilder.toString(), "", snapObj, fsObj, storageObj);
}
} catch (Exception e) {
_logger.error("Caught an exception while trying to updateStatus for VNXeModifyExportJob", e);
setErrorStatus("Encountered an internal error during update export rules job status processing : " + e.getMessage());
} finally {
super.updateStatus(jobContext);
}
}
/**
* update FileShare after exported in VNXe
*
* @param fsOjb fileShare object in vipr
* @param dbClient DbClient
* @param vnxeApiClient VNXeApiClient
*/
private void updateExportRules(VNXeApiClient vnxeApiClient, DbClient dbClient, FileShare fileObj, VNXeNfsShare nfsShare) {
_logger.info("updating file export. ");
try {
// Modify Existing Exports
FileExportRule newRule = new FileExportRule();
URI snapshotId = null;
if (!isFile) {
snapshotId = getTaskCompleter().getId();
}
// Copy the properties to build the index id to query DB for existing Export Rule
copyPropertiesToSave(newRule, rule, fileObj, dbClient, snapshotId);
newRule = getAvailableExportRule(newRule, dbClient);
// Remove the existing and create the new one.
// Don't Update the existing one as persist object will create a new StringSet rather
// it updates the existing one with new information and upon keeping/appending to old one.
if (newRule != null) {
newRule.setInactive(true);
_logger.info("Removing Existing DB Export Rule {}", rule);
dbClient.persistObject(newRule);
}
if (!isDeleteRule) {
newRule = new FileExportRule();
newRule.setId(URIUtil.createId(FileExportRule.class));
if (nfsShare != null) {
if (nfsShare.getReadOnlyHosts() != null) {
Set<String> hosts = new HashSet<String>();
for (VNXeBase hostId : nfsShare.getReadOnlyHosts()) {
hosts.add(vnxeApiClient.getHostById(hostId.getId()).getName());
}
rule.setReadOnlyHosts(hosts);
}
if (nfsShare.getReadWriteHosts() != null) {
Set<String> hosts = new HashSet<String>();
for (VNXeBase hostId : nfsShare.getReadWriteHosts()) {
hosts.add(vnxeApiClient.getHostById(hostId.getId()).getName());
}
rule.setReadWriteHosts(hosts);
}
if (nfsShare.getRootAccessHosts() != null) {
Set<String> hosts = new HashSet<String>();
for (VNXeBase hostId : nfsShare.getRootAccessHosts()) {
hosts.add(vnxeApiClient.getHostById(hostId.getId()).getName());
}
rule.setRootHosts(hosts);
}
}
// Now, Copy the properties again into the rule came out of DB, before updating.
copyPropertiesToSave(newRule, rule, fileObj, dbClient, snapshotId);
_logger.info("Storing New DB Export Rule {}", newRule);
dbClient.createObject(newRule);
}
} catch (Exception e) {
_logger.info("Error While executing CRUD Operations {}", e);
}
}
private void updateFSExport(FileShare fsObj, DbClient dbClient, FileExport newExport) {
_logger.info("updating file export. ");
FSExportMap exports = fsObj.getFsExports();
if (exports == null) {
exports = new FSExportMap();
}
FileExport exportToBeUpdated = exports.get(newExport.getFileExportKey());
if (exportToBeUpdated != null) {
List<String> clients = new ArrayList<String>();
if (rule.getReadOnlyHosts() != null) {
clients.addAll(rule.getReadOnlyHosts());
}
if (rule.getReadWriteHosts() != null) {
clients.addAll(rule.getReadWriteHosts());
}
if (rule.getRootHosts() != null) {
clients.addAll(rule.getRootHosts());
}
exportToBeUpdated.setClients(clients);
exports.put(newExport.getFileExportKey(), exportToBeUpdated);
fsObj.setFsExports(exports);
dbClient.persistObject(fsObj);
}
}
private void updateSnapshotExport(Snapshot snapObj, DbClient dbClient, FileExport newExport) {
_logger.info("updating file export. ");
FSExportMap exports = snapObj.getFsExports();
if (exports == null) {
exports = new FSExportMap();
}
FileExport exportToBeUpdated = exports.get(newExport.getFileExportKey());
if (exportToBeUpdated != null) {
List<String> clients = new ArrayList<String>();
if (rule.getReadOnlyHosts() != null) {
clients.addAll(rule.getReadOnlyHosts());
}
if (rule.getReadWriteHosts() != null) {
clients.addAll(rule.getReadWriteHosts());
}
if (rule.getRootHosts() != null) {
clients.addAll(rule.getRootHosts());
}
exportToBeUpdated.setClients(clients);
exports.put(newExport.getFileExportKey(), exportToBeUpdated);
snapObj.setFsExports(exports);
dbClient.persistObject(snapObj);
}
}
private FileExportRule getAvailableExportRule(FileExportRule exportRule, DbClient dbClient)
throws URISyntaxException {
String exportIndex = exportRule.getFsExportIndex();
if (!isFile) {
exportIndex = exportRule.getSnapshotExportIndex();
}
_logger.info("Retriving DB Model using its index {}", exportIndex);
FileExportRule rule = null;
URIQueryResultList result = new URIQueryResultList();
if (!isFile) {
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getSnapshotExportRuleConstraint(exportIndex), result);
} else {
dbClient.queryByConstraint(AlternateIdConstraint.Factory
.getFileExportRuleConstraint(exportIndex), result);
}
Iterator<URI> it = result.iterator();
while (it.hasNext()) {
if (result.iterator().hasNext()) {
rule = dbClient.queryObject(FileExportRule.class, it.next());
if (rule != null && !rule.getInactive()) {
_logger.info("Existing DB Model found {}", rule);
break;
}
}
}
return rule;
}
private void copyPropertiesToSave(FileExportRule dest, ExportRule orig,
FileShare fs, DbClient dbClient, URI snapshotId) {
_logger.info("Origin {}", orig.toString());
// This export path is the one that is figured out at the device.
// Make sure you set the path on args object while doing the operation. Check for <Device>FileStorageDeviceXXX.java
dest.setExportPath(exportPath);
dest.setSecFlavor(orig.getSecFlavor());
dest.setAnon(orig.getAnon());
if (orig.getReadOnlyHosts() != null && !orig.getReadOnlyHosts().isEmpty()) {
dest.setReadOnlyHosts(new StringSet(orig.getReadOnlyHosts()));
_logger.info("Read Only Hosts {}", dest.getReadOnlyHosts());
}
if (orig.getReadWriteHosts() != null && !orig.getReadWriteHosts().isEmpty()) {
dest.setReadWriteHosts(new StringSet(orig.getReadWriteHosts()));
_logger.info("Read Write Hosts {}", dest.getReadWriteHosts());
}
if (orig.getRootHosts() != null && !orig.getRootHosts().isEmpty()) {
dest.setRootHosts(new StringSet(orig.getRootHosts()));
_logger.info("Root hosts {}", dest.getRootHosts());
}
// Set this always at the end -- Thats how the model is defined.
if (!isFile) {
dest.setSnapshotId(snapshotId);
} else {
dest.setFileSystemId(fs.getId());
}
// Figure out Storage Port Network id to build the mount point.
StoragePort storagePort = dbClient.queryObject(StoragePort.class, fs.getStoragePort());
String mountPoint = ExportUtils.getFileMountPoint(storagePort.getPortNetworkId(), exportPath);
dest.setMountPoint(mountPoint);
dest.setDeviceExportId(orig.getDeviceExportId());
_logger.info("Dest After {}", dest.toString());
}
}