/*
* Copyright (c) 2008-2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.Controller;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.DiscoveredDataObject.Type;
import com.emc.storageos.db.client.model.DiscoveredSystemObject;
import com.emc.storageos.db.client.model.StorageProvider;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.db.exceptions.RetryableDatabaseException;
import com.emc.storageos.exceptions.ClientControllerException;
import com.emc.storageos.impl.AbstractDiscoveredSystemController;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.resources.InternalException;
import com.emc.storageos.svcs.errorhandling.resources.ServiceCode;
import com.emc.storageos.volumecontroller.ApplicationAddVolumeList;
import com.emc.storageos.volumecontroller.ArrayAffinityAsyncTask;
import com.emc.storageos.volumecontroller.AsyncTask;
import com.emc.storageos.volumecontroller.BlockController;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl.Lock;
import com.emc.storageos.volumecontroller.impl.monitoring.MonitoringJob;
import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.DataCollectionJobUtil;
import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.DataCollectionScanJob;
import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.MonitorTaskCompleter;
import com.emc.storageos.volumecontroller.impl.plugins.discovery.smis.ScanTaskCompleter;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
import com.emc.storageos.workflow.WorkflowStepCompleter;
public class BlockControllerImpl extends AbstractDiscoveredSystemController implements BlockController {
private static final Logger _log = LoggerFactory.getLogger(BlockControllerImpl.class);
private Set<BlockController> _deviceImpl;
private Dispatcher _dispatcher;
private DbClient _dbClient;
private DataCollectionJobUtil _util;
public void setDeviceImpl(Set<BlockController> deviceImpl) {
_deviceImpl = deviceImpl;
}
public void setDispatcher(Dispatcher dispatcher) {
_dispatcher = dispatcher;
}
public void setDbClient(DbClient dbClient) {
_dbClient = dbClient;
}
public void setUtil(DataCollectionJobUtil util) {
_util = util;
}
/**
* Dummy implementation that just returns the first controller that implements this device
*
* @param device
* @return
* @throws ControllerException
*/
@Override
protected Controller lookupDeviceController(DiscoveredSystemObject device)
throws ControllerException {
if (device == null) {
throw ClientControllerException.fatals.unableToLookupStorageDeviceIsNull();
}
BlockController bc = _deviceImpl.iterator().next();
if (bc == null) {
throw ClientControllerException.fatals.unableToLocateDeviceController("BlockController");
}
return bc;
}
/**
* Dummy implementation that just returns the controller.
*
* @return
* @throws ControllerException
*/
protected Controller lookupDeviceController() throws ControllerException {
BlockController bc = _deviceImpl.iterator().next();
if (bc == null) {
throw ClientControllerException.fatals.unableToLocateDeviceController("BlockController");
}
return bc;
}
/**
* Puts the operation in the zkQueue so it can Dispatch'd to a Device Controller
*
* @param methodName
* @param args
* @throws ControllerException
*/
private void blockRMI(String methodName, Object... args) throws InternalException {
queueTask(_dbClient, StorageSystem.class, _dispatcher, methodName, args);
}
@Override
public void createVolumes(URI storage, URI pool, List<URI> volumes, VirtualPoolCapabilityValuesWrapper capabilities, String opId)
throws InternalException {
blockRMI("createVolumes", storage, pool, volumes, capabilities, opId);
}
@Override
public void modifyVolumes(URI storage, URI pool, List<URI> volumes, String opId) throws InternalException {
blockRMI("modifyVolumes", storage, pool, volumes, opId);
}
@Override
public void createMetaVolume(URI storage, URI pool, URI volume, VirtualPoolCapabilityValuesWrapper capabilities, String opId)
throws InternalException {
blockRMI("createMetaVolume", storage, pool, volume, capabilities, opId);
}
@Override
public void rollBackCreateMetaVolume(URI systemURI, URI volumeURI, String createStepId, String opId) throws InternalException
{
blockRMI("rollBackCreateMetaVolume", systemURI, volumeURI, createStepId, opId);
}
@Override
public void createMetaVolumes(URI storage, URI pool, List<URI> volumes, VirtualPoolCapabilityValuesWrapper capabilities, String opId)
throws InternalException {
blockRMI("createMetaVolumes", storage, pool, volumes, capabilities, opId);
}
@Override
public void rollBackCreateVolumes(URI systemURI, List<URI> volumeURIs, String opId) throws InternalException
{
blockRMI("rollBackCreateVolume", systemURI, volumeURIs, opId);
}
@Override
public void expandBlockVolume(URI storage, URI pool, URI volume, Long size, String opId)
throws InternalException {
blockRMI("expandBlockVolume", storage, pool, volume, size, opId);
}
@Override
public void expandVolume(URI storage, URI pool, URI volume, Long size, String opId)
throws InternalException {
blockRMI("expandVolume", storage, pool, volume, size, opId);
}
@Override
public void rollBackExpandVolume(URI systemURI, URI volumeURI, String expandStepId, String opId) throws InternalException
{
blockRMI("rollBackExpandVolume", systemURI, volumeURI, expandStepId, opId);
}
@Override
public void deleteVolumes(URI storage, List<URI> volumes, String opId)
throws InternalException {
blockRMI("deleteVolumes", storage, volumes, opId);
}
@Override
public void createSingleSnapshot(URI storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, String opId)
throws ControllerException {
blockRMI("createSingleSnapshot", storage, snapshotList, createInactive, readOnly, opId);
}
@Override
public void createSnapshot(URI storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, String opId)
throws InternalException {
blockRMI("createSnapshot", storage, snapshotList, createInactive, readOnly, opId);
}
@Override
public void activateSnapshot(URI storage, List<URI> snapshotList, String opId)
throws InternalException {
try {
// Direct RMI call to expedite this call without any potential distribute-Q delay
StorageSystem storageSystem =
_dbClient.queryObject(StorageSystem.class, storage);
Controller controller = lookupDeviceController(storageSystem);
BlockController blkcontroller = (BlockController) controller;
blkcontroller.activateSnapshot(storage, snapshotList, opId);
} catch (RetryableDatabaseException e) {
if (e.getServiceCode() == ServiceCode.DBSVC_CONNECTION_ERROR) {
// netflix curator ConnectionException is not serializable
// and thus should not be sent back to rmi client.
_log.error("Failed to queue task due to dbsvc disconnected. Error: {}", e.getMessage());
_log.error(e.getMessage(), e);
throw DatabaseException.retryables.connectionFailed();
}
throw e;
}
}
@Override
public void deleteSnapshot(URI storage, URI snapshot, String opId)
throws InternalException {
blockRMI("deleteSnapshot", storage, snapshot, opId);
}
@Override
public void restoreVolume(URI storage, URI pool, URI volume, URI snapshot, Boolean updateOpStatus, String syncDirection, String opId)
throws InternalException {
blockRMI("restoreVolume", storage, pool, volume, snapshot, updateOpStatus, opId);
}
@Override
public void resyncSnapshot(URI storage, URI volume, URI snapshot, Boolean updateOpStatus, String opId) throws InternalException {
blockRMI("resyncSnapshot", storage, volume, snapshot, updateOpStatus, opId);
}
@Override
public void resyncFullCopy(URI storage, List<URI> clone, Boolean updateOpStatus, String opId)
throws InternalException {
blockRMI("resyncFullCopy", storage, clone, updateOpStatus, opId);
}
@Override
public void connectStorage(URI storage) throws InternalException {
blockRMI("connectStorage", storage);
}
@Override
public void disconnectStorage(URI storage) throws InternalException {
blockRMI("disconnectStorage", storage);
}
@Override
public void createMirror(URI storage, List<URI> mirrorList, Boolean isCG, Boolean createInactive, String opId) throws InternalException {
blockRMI("createMirror", storage, mirrorList, createInactive, opId);
}
@Override
public void attachNativeContinuousCopies(URI storage, URI sourceVolume, List<URI> mirrorList, String opId) throws InternalException {
blockRMI("attachNativeContinuousCopies", storage, sourceVolume, mirrorList, opId);
}
@Override
public void detachNativeContinuousCopies(URI storage, List<URI> mirrors, List<URI> promotees,
String opId) throws InternalException {
blockRMI("detachNativeContinuousCopies", storage, mirrors, promotees, opId);
}
@Override
public void pauseNativeContinuousCopies(URI storage, List<URI> mirrors, Boolean sync,
String opId) throws InternalException {
try {
// Direct RMI call to expedite this call without any potential distribute-Q delay
StorageSystem storageSystem =
_dbClient.queryObject(StorageSystem.class, storage);
Controller controller = lookupDeviceController(storageSystem);
BlockController blkcontroller = (BlockController) controller;
blkcontroller.pauseNativeContinuousCopies(storage, mirrors, sync, opId);
} catch (RetryableDatabaseException e) {
if (e.getServiceCode() == ServiceCode.DBSVC_CONNECTION_ERROR) {
// netflix curator ConnectionException is not serializable
// and thus should not be sent back to rmi client.
_log.error("Failed to queue task due to dbsvc disconnected. Error: {}", e.getMessage());
_log.error(e.getMessage(), e);
throw DatabaseException.retryables.connectionFailed();
}
throw e;
}
}
@Override
public void establishVolumeAndNativeContinuousCopyGroupRelation(URI storage, URI sourceVolume, URI mirror, String opId)
throws InternalException {
blockRMI("establishVolumeAndNativeContinuousCopyGroupRelation", storage, sourceVolume, mirror, opId);
}
@Override
public void establishVolumeAndSnapshotGroupRelation(URI storage, URI sourceVolume, URI snapshot, String opId) throws InternalException {
blockRMI("establishVolumeAndSnapshotGroupRelation", storage, sourceVolume, snapshot, opId);
}
@Override
public void resumeNativeContinuousCopies(URI storage, List<URI> mirrors, String opId) throws InternalException {
blockRMI("resumeNativeContinuousCopies", storage, mirrors, opId);
}
@Override
public void detachMirror(URI storage, List<URI> mirrorList, Boolean isCG, Boolean deleteGroup, String opId) throws InternalException {
blockRMI("detachMirror", storage, mirrorList, isCG, deleteGroup, opId);
}
@Override
public void deleteMirror(URI storage, List<URI> mirrorList, Boolean isCG, String opId) throws InternalException {
blockRMI("deleteMirror", storage, mirrorList, isCG, opId);
}
@Override
public void deactivateMirror(URI storage, List<URI> mirrorList, List<URI> promotees, Boolean isCG, String opId)
throws InternalException {
blockRMI("deactivateMirror", storage, mirrorList, promotees, isCG, opId);
}
@Override
public void createFullCopy(URI storage, List<URI> fullCopyVolumes, Boolean createInactive, String opId)
throws InternalException {
blockRMI("createFullCopy", storage, fullCopyVolumes, createInactive, opId);
}
@Override
public void deleteConsistencyGroup(URI storage, URI consistencyGroup, Boolean markInactive, String opId) throws InternalException {
blockRMI("deleteConsistencyGroup", storage, consistencyGroup, markInactive, opId);
}
@Override
public void createConsistencyGroup(URI storage, URI consistencyGroup, String opId) throws InternalException {
blockRMI("createConsistencyGroup", storage, consistencyGroup, opId);
}
@Override
public void activateFullCopy(URI storage, List<URI> fullCopy, String opId) {
try {
// Direct RMI call to expedite this call without any potential distribute-Q delay
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, storage);
Controller controller = lookupDeviceController(storageSystem);
BlockController blkcontroller = (BlockController) controller;
blkcontroller.activateFullCopy(storage, fullCopy, opId);
} catch (RetryableDatabaseException e) {
if (e.getServiceCode() == ServiceCode.DBSVC_CONNECTION_ERROR) {
// netflix curator ConnectionException is not serializable
// and thus should not be sent back to rmi client.
_log.error("Failed to queue task due to dbsvc disconnected. Error: {}", e.getMessage());
_log.error(e.getMessage(), e);
throw DatabaseException.retryables.connectionFailed();
}
throw e;
}
}
@Override
public void detachFullCopy(URI storage, List<URI> fullCopy, String opId) {
blockRMI("detachFullCopy", storage, fullCopy, opId);
}
@Override
public void establishVolumeAndFullCopyGroupRelation(URI storage, URI sourceVolume, URI fullCopy, String opId) {
blockRMI("establishVolumeAndFullCopyGroupRelation", storage, sourceVolume, fullCopy, opId);
}
@Override
public Integer checkSyncProgress(URI storage, URI source, URI target, String opId) {
try {
StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, storage);
Controller controller = lookupDeviceController(storageSystem);
BlockController blkcontroller = (BlockController) controller;
return blkcontroller.checkSyncProgress(storage, source, target, opId);
} catch (RetryableDatabaseException e) {
if (e.getServiceCode() == ServiceCode.DBSVC_CONNECTION_ERROR) {
// netflix curator ConnectionException is not serializable
// and thus should not be sent back to rmi client.
_log.error("Failed to queue task due to dbsvc disconnected. Error: {}", e.getMessage());
_log.error(e.getMessage(), e);
throw DatabaseException.retryables.connectionFailed();
}
throw e;
}
}
/**
* {@inheritDoc}
*/
@Override
public void discoverStorageSystem(AsyncTask[] tasks)
throws ControllerException {
try {
if (tasks[0] instanceof ArrayAffinityAsyncTask) {
ControllerServiceImpl.scheduleDiscoverJobs(tasks, Lock.ARRAYAFFINITY_DISCOVER_COLLECTION_LOCK,
ControllerServiceImpl.ARRAYAFFINITY_DISCOVERY);
} else {
ControllerServiceImpl.scheduleDiscoverJobs(tasks, Lock.DISCOVER_COLLECTION_LOCK, ControllerServiceImpl.DISCOVERY);
}
} catch (Exception e) {
_log.error(
"Problem in discoverStorageSystem due to {} ",
e.getMessage());
throw ClientControllerException.fatals.unableToScheduleDiscoverJobs(tasks, e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void scanStorageProviders(AsyncTask[] tasks)
throws ControllerException {
try {
Map<String, DataCollectionScanJob> scanJobByInterfaceType = new HashMap<String, DataCollectionScanJob>();
for (AsyncTask task : tasks) {
ScanTaskCompleter completer = new ScanTaskCompleter(task);
StorageProvider provider = _dbClient.queryObject(StorageProvider.class, completer.getId());
if (provider != null && !provider.getInactive()) {
if (scanJobByInterfaceType.get(provider.getInterfaceType()) == null) {
scanJobByInterfaceType.put(provider.getInterfaceType(), new DataCollectionScanJob());
}
scanJobByInterfaceType.get(provider.getInterfaceType()).addCompleter(completer);
}
}
for (DataCollectionScanJob job : scanJobByInterfaceType.values()) {
_util.scheduleScanningJobs(job);
}
} catch (Exception e) {
throw ClientControllerException.fatals.unableToScanSMISProviders(tasks, "BlockController", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void addStorageSystem(URI storage, URI[] providers, boolean primaryProvider, String opId)
throws InternalException {
blockRMI("addStorageSystem", storage, providers, primaryProvider, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void startMonitoring(AsyncTask task, Type deviceType)
throws ControllerException {
try {
MonitoringJob job = new MonitoringJob();
job.setCompleter(new MonitorTaskCompleter(task));
job.setDeviceType(deviceType);
ControllerServiceImpl.enqueueMonitoringJob(job);
} catch (Exception e) {
throw ClientControllerException.fatals.unableToMonitorSMISProvider(task, deviceType.toString(), e);
}
}
@Override
public void noActionRollBackStep(URI deviceURI, String opID) {
_log.info("Running empty Roll back step for storage system {}", deviceURI);
WorkflowStepCompleter.stepSucceded(opID);
}
@Override
public void updateConsistencyGroup(URI storage, URI consistencyGroup,
List<URI> addVolumesList,
List<URI> removeVolumesList, String task) {
blockRMI("updateConsistencyGroup", storage, consistencyGroup, addVolumesList,
removeVolumesList, task);
}
@Override
public boolean validateStorageProviderConnection(String ipAddress,
Integer portNumber, String interfaceType) {
// Making a direct call to get connection status.
Controller controller = lookupDeviceController();
BlockController blkcontroller = (BlockController) controller;
return blkcontroller.validateStorageProviderConnection(ipAddress, portNumber, interfaceType);
}
@Override
public void restoreFromFullCopy(URI storage, List<URI> clones,
Boolean updateOpStatus, String opId) throws InternalException {
blockRMI("restoreFromFullCopy", storage, clones, updateOpStatus, opId);
}
@Override
public void updateApplication(URI storage, ApplicationAddVolumeList addVolList, URI application,
String opId) throws ControllerException {
blockRMI("updateApplication", storage, addVolList, application, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void createSnapshotSession(URI systemURI, URI snapSessionURI, List<List<URI>> sessionSnapshotURIs,
String copyMode, String opId)
throws InternalException {
blockRMI("createSnapshotSession", systemURI, snapSessionURI, sessionSnapshotURIs, copyMode, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void linkNewTargetVolumesToSnapshotSession(URI systemURI, URI snapSessionURI, List<List<URI>> snapshotURIs,
String copyMode, String opId) {
blockRMI("linkNewTargetVolumesToSnapshotSession", systemURI, snapSessionURI, snapshotURIs, copyMode, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void relinkTargetsToSnapshotSession(URI systemURI, URI tgtSnapSessionURI, List<URI> snapshotURIs,
Boolean updateStatus, String opId) throws InternalException {
blockRMI("relinkTargetsToSnapshotSession", systemURI, tgtSnapSessionURI, snapshotURIs, updateStatus, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void unlinkTargetsFromSnapshotSession(URI systemURI, URI snapSessionURI,
Map<URI, Boolean> snapshotDeletionMap, OperationTypeEnum opType, String opId) {
blockRMI("unlinkTargetsFromSnapshotSession", systemURI, snapSessionURI, snapshotDeletionMap, opType, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void restoreSnapshotSession(URI systemURI, URI snapSessionURI, Boolean updateStatus, String opId) {
blockRMI("restoreSnapshotSession", systemURI, snapSessionURI, updateStatus, opId);
}
/**
* {@inheritDoc}
*/
@Override
public void deleteSnapshotSession(URI systemURI, URI snapSessionURI, String opId) {
blockRMI("deleteSnapshotSession", systemURI, snapSessionURI, opId);
}
@Override
public void setInitiatorAlias(URI systemURI, URI initiatorURI, String initiatorAlias) throws Exception {
// Making a direct call to set alias.
Controller controller = lookupDeviceController();
BlockController blkcontroller = (BlockController) controller;
blkcontroller.setInitiatorAlias(systemURI, initiatorURI, initiatorAlias);
}
@Override
public String getInitiatorAlias(URI systemURI, URI initiatorURI) throws Exception {
// Making a direct call to get alias.
Controller controller = lookupDeviceController();
BlockController blkcontroller = (BlockController) controller;
return blkcontroller.getInitiatorAlias(systemURI, initiatorURI);
}
}