/* * Copyright (c) 2015-2016 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.file; import static java.util.Arrays.asList; import java.net.URI; import java.util.HashSet; 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.model.FileShare; import com.emc.storageos.db.client.model.FileShare.MirrorStatus; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.Operation.Status; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.security.audit.AuditLogManager; import com.emc.storageos.security.audit.AuditLogManagerFactory; import com.emc.storageos.services.OperationTypeEnum; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent; import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager; import com.emc.storageos.volumecontroller.impl.monitoring.cim.enums.RecordType; public class MirrorFileTaskCompleter extends TaskCompleter { /** * Reference to logger */ private static final Logger _logger = LoggerFactory .getLogger(MirrorFileTaskCompleter.class); public MirrorFileTaskCompleter(Class clazz, List<URI> ids, String opId, URI stoageUri) { super(clazz, ids, opId); this.storageUri = stoageUri; } public MirrorFileTaskCompleter(Class clazz, List<URI> ids, String opId) { super(clazz, ids, opId); } public MirrorFileTaskCompleter(Class clazz, URI id, String opId, URI stoageUri) { super(clazz, id, opId); this.storageUri = stoageUri; } private static final String EVENT_SERVICE_TYPE = "file"; private static final String EVENT_SERVICE_SOURCE = "FileController"; protected FileShare.MirrorStatus mirrorSyncStatus = FileShare.MirrorStatus.OTHER; private URI storageUri; public void setStorageUri(URI storageUri) { this.storageUri = storageUri; } public URI getStorageUri() { return storageUri; } protected MirrorStatus getMirrorSyncStatus() { return mirrorSyncStatus; } protected void setMirrorSyncStatus(MirrorStatus mirrorSyncStatus) { this.mirrorSyncStatus = mirrorSyncStatus; } public MirrorFileTaskCompleter(Class clazz, URI id, String opId) { super(clazz, id, opId); } public MirrorFileTaskCompleter(URI sourceURI, URI targetURI, String opId) { super(FileShare.class, asList(sourceURI, targetURI), opId); } protected List<FileShare> fileShareCache; private DbClient dbClient; public DbClient getDbClient() { return dbClient; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } protected List<FileShare> fileshareCache; @Override protected void complete(DbClient dbClient, Status status, ServiceCoded coded) throws DeviceControllerException { setStatus(dbClient, status, coded); if (isNotifyWorkflow()) { updateWorkflowStatus(status, coded); } updateFileSystemStatus(dbClient, status); } /** * Update the filesystem status * * @param dbClient * @param status */ protected void updateFileSystemStatus(DbClient dbClient, Operation.Status status) { try { if (Operation.Status.ready.equals(status)) { List<FileShare> fileshares = dbClient.queryObject(FileShare.class, getIds()); for (FileShare fs : fileshares) { fs.setMirrorStatus(getFileMirrorStatusForSuccess(fs)); fs.setAccessState(getFileShareAccessStateForSuccess(fs).name()); } dbClient.updateObject(fileshares); _logger.info("Updated Mirror status for fileshares: {}", getIds()); } } catch (Exception e) { _logger.info("Not updating fileshare mirror link status for fileshares: {}", getIds(), e); } } /** * Record FileShare related event and audit * * @param dbClient db client * @param opType operation type * @param status operation status * @param extParam parameters array from which we could generate detail audit message */ public void recordMirrorOperation(DbClient dbClient, OperationTypeEnum opType, Operation.Status status, Object... extParam) { try { boolean opStatus = (Operation.Status.ready == status) ? true : false; String evType; evType = opType.getEvType(opStatus); String evDesc = opType.getDescription(); String opStage = AuditLogManager.AUDITOP_END; _logger.info("opType: {} detail: {}", opType.toString(), evType.toString() + ':' + evDesc); recordBourneMirrorEvent(dbClient, getId(), evType, status, evDesc); switch (opType) { case CREATE_FILE_MIRROR: case START_FILE_MIRROR: case SUSPEND_FILE_MIRROR: case DETACH_FILE_MIRROR: case PAUSE_FILE_MIRROR: case RESUME_FILE_MIRROR: case FAILOVER_FILE_MIRROR: case STOP_FILE_MIRROR: case FAILBACK_FILE_MIRROR: case RESYNC_FILE_MIRROR: case REFRESH_FILE_MIRROR: case MODIFY_FILE_MIRROR_RPO: auditFile(dbClient, opType, opStatus, opStage, extParam); break; default: _logger.error("unrecognized Mirror operation type"); } } catch (Exception e) { _logger.error("Failed to record Mirror operation {}, err: {}", opType.toString(), e); } } /** * Record audit log for file service * * @param auditType Type of AuditLog * @param operationalStatus Status of operation * @param description Description for the AuditLog * @param descparams Description paramters */ public static void auditFile(DbClient dbClient, OperationTypeEnum auditType, boolean operationalStatus, String description, Object... descparams) { AuditLogManager auditMgr = AuditLogManagerFactory.getAuditLogManager(); auditMgr.setDbClient(dbClient); auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE, auditType, System.currentTimeMillis(), operationalStatus ? AuditLogManager.AUDITLOG_SUCCESS : AuditLogManager.AUDITLOG_FAILURE, description, descparams); } /** * * @param dbClient * @param evtType * @param status * @param desc * @throws Exception */ public void recordBourneMirrorEvent(DbClient dbClient, URI fileUri, String evtType, Operation.Status status, String desc) throws Exception { RecordableEventManager eventManager = new RecordableEventManager(); eventManager.setDbClient(dbClient); FileShare fsObj = dbClient.queryObject(FileShare.class, fileUri); RecordableBourneEvent event = ControllerUtils.convertToRecordableBourneEvent(fsObj, evtType, desc, "", dbClient, EVENT_SERVICE_TYPE, RecordType.Event.name(), EVENT_SERVICE_SOURCE); try { eventManager.recordEvents(event); _logger.info("ViPR {} event recorded", evtType); } catch (Exception ex) { _logger.error( "Failed to record event. Event description: {}. Error: ", evtType, ex); } } protected List<FileShare> getFileShares() { if (fileShareCache == null) { fileShareCache = getDbClient().queryObject(FileShare.class, getIds()); } return fileShareCache; } protected Set<String> getFileShareIds() { Set<String> fileShareIds = new HashSet<String>(); for (FileShare fileshare : fileShareCache) { fileShareIds.add(fileshare.getNativeGuid()); } return fileShareIds; } protected FileShare getTargetFileShare() { for (FileShare fs : getFileShares()) { if (!NullColumnValueGetter.isNullNamedURI(fs.getParentFileShare()) && !fs.getParentFileShare().getURI().toString().equalsIgnoreCase("null")) { return fs; } } throw new IllegalStateException("Expected a target FileShare with an non-null Replication parent"); } protected FileShare getSourceFileShare() { for (FileShare fs : getFileShares()) { if (fs.getMirrorfsTargets() != null) { return fs; } } throw new IllegalStateException("Expected a source FileShare with an non-null Replication parent"); } protected String getFileMirrorStatusForSuccess(FileShare fs) { return this.mirrorSyncStatus.name(); } /** * Setting access state is based on the personality and failover state of the fileshare, regardless of operation * * @param fs a fileshare impacted by Mirror operation * @return file access state for that fileshare */ protected FileShare.FileAccessState getFileShareAccessStateForSuccess(FileShare fs) { // If this fileshare is a source and exported to a host, the is write-disabled. Otherwise it is readwrite. if (fs.getPersonality().equals(FileShare.PersonalityTypes.SOURCE.toString()) && fs.getMirrorStatus().equals(FileShare.MirrorStatus.FAILED_OVER.name())) { return FileShare.FileAccessState.NOT_READY; } else if (fs.getPersonality().equals(FileShare.PersonalityTypes.TARGET.toString()) && fs.getMirrorStatus().equals(FileShare.MirrorStatus.FAILED_OVER.name())) { // A target fileshare in any state other than FAILED_OVER is write-disabled or not-ready. return FileShare.FileAccessState.NOT_READY; } // Any other state is READWRITE return FileShare.FileAccessState.READWRITE; } }