Skip to end of metadata
Go to start of metadata

What is inside Southbound SDK

You will need to  use CoprHD storage driver SDK to write storage driver for your type of storage system.

For each CoprHD release, the are two jar files(attached in this page), jar file with compiled SDK code and jar file with SDK source files. Check "comment" section for each jar file in attachment to see CoprHD version which supports this SDK.

Add compiled SDK jar to your class path and add SDK source jar to your source location.

The SDK jar has the following content: 

  • set of interfaces which driver have to implement
  • set of object model classes which are used in interface methods
  • interfaces for CoprHD services available to storage driver
  • several supporting classes (DriverTask, CapabilityInstance, etc.) 
  • public libraries which are used by SDK
  • abstract base class for storage driver

 

Development Support

Email distribution list for driver's developers: southboundsdk@emc.com .

There is also HipChat room at https://coprhd.hipchat.com/rooms dedicated for support driver developers.  

How to create storage driver

Create your driver class as extension of DefaultStorageDriver.java class. Declare which storage driver interface the driver implements — BlockStorageDriver, FileStorageDriver or both interfaces. When CoprHD instantiates the driver, it will set Registry and LockManager into the driver (this is done through base class methods). These two services are provided by CoprHD for driver to use. Registry provides distributed persisted storage for driver data. LockManager provides distributed lock management. This is up to driver when and how to use these two services provided by CoprHD.

NewStorageDriver 

 


public class NewStorageDriver extends DefaultStorageDriver implements BlockStorageDriver {
 
    private static final Logger _log = LoggerFactory.getLogger(NewStorageDriver.class);
 
    
 
   .....................................
}


 

Implement methods of DiscoveryDriver.java and BlockStorageDriver.java in your storage driver class. Follow method specification defined in interface classes.

There are two types of methods in these interface classes. Methods which return results and methods which return instance of DriverTask.

Methods which return DriverTask allow for non-blocking asynchronous implementation. For long running requests you can choose to return DriverTask to client immediately and continue to execute the request asynchronously. DriverTask should be set to QUEUED/PROVISIONING state. CoprHD will call driver periodically to refresh task state. When driver completes request execution, driver should set all required data in provided arguments according to API specification and after that set task state to one of terminal states. In cases when a request can be executed synchronously, set all required data according to API specification and return task in terminal state. It is recommended to implement long running requests in asynchronous non-blocking way.

When your driver does not support request defined in the API, return task in failed state and provide "non supported" error message in the task.

Note: We continue to define capability model specification. In the first version of storage driver API, all arguments in API methods related to capability instances are set to null.

Provisioning of Storage Volumes

Let us look in possible implementation of one of API method  BlockStorageDriver.createVolumes():

BlockStorageDriver.createVolumes()

 


/**
 * Create storage volumes with a given set of capabilities.
 * Before completion of the request, set all required data for provisioned volumes in "volumes" parameter.
 *
 * @param volumes Input/output argument for volumes.
 * @param capabilities Input argument for capabilities.
 * @return task
 */
public DriverTask createVolumes(List<StorageVolume> volumes, List<CapabilityInstance> capabilities);


When this method is called client passes StorageVolume list container with storage volumes to provision and a list of vendor specific capabilities required for these volumes. For the first version we set capabilities in API requests to null. List of volumes is input/output parameter to the driver. Several volume properties are set by client to inform the driver how to provision volumes, other volume properties should be set by driver when provisioning is complete.  Driver should return instance of DriverTask to client. The task should have state property corresponding to actual state of request execution.

StorageVolume attributes set by client when method to create storage volumes is called. These attributes are input attributes and should be used by driver when storage volume is provisioned:

  • storagePoolId
  • storageSystemId
  • consistencyGroup

  • requestedCapacity
  • thinlyProvisioned

  • thinVolumePreAllocationSize

StorageVolume attributes which should be set by driver prior to completion of provisioning request:

  • nativeId 
  • displayName
  • accessStatus
  • provisionedCapacity
  • allocatedCapacity

Assume that create volumes provisioning request is implemented by the driver in non-blocking way. In this case, the driver returns task in one of executing states back to client. The driver continue request execution asynchronously. Client queues the task for monitoring. Client periodically checks state of the task. At some point driver completes volume provisioning, sets all required properties in passed volume list and sets task status to completed state. When client finds that the task was completed, client reads volumes from the passed volume list and processes provisioning results.

Auto Tiering Policy Support

The storage driver SDK includes support for the discovery of auto tiering policies and their use in storage volume placement during volume creation.

Discovering Auto Tiering Policies

The storage driver SDK supports the discovery of auto tiering policies during Storage Pool discovery via the discoverStoragePools driver API . In order for the controller to discover auto tiering policies, the SDK driver must add a CapabilityInstance for each auto tiering policy supported by that storage pool in the StoragePool instance representing that pool. Each CapabilityInstance must specify the unique id of a CapabilityDefinition. In this case, it must be the unique id of the AutoTieringPolicyCapabilityDefinition. Further, the CapabilityInstance must have a value in its properties map for each of the auto tiering policy properties, as defined in the AutoTieringPolicyCapabilityDefinition. Currently, these properties for an auto tiering policy are "POLICY_ID" and "PROVISIONING_TYPE". During discovery, the controller will get the capabilities for each returned StoragePool and use the CapabilityDefinition id to determine if it is an auto tiering policy capability. If so, it will use the definition to get the values for the auto tiering policy properties. Upon processing all storage pools returned by the SDK driver, the controller will have built a unique list of new and/or updated AutoTieringPolicy instances, as determined by the POLICY_ID.

 

Driver Example:

@Override
public DriverTask discoverStoragePools(StorageSystem storageSystem, List<StoragePool> storagePools) {
    StoragePool pool = new StoragePool();
    // Set other storage pool properties
        
    // Add auto tiering policy capabilities
    AutoTieringPolicyCapabilityDefinition capabilityDefinition = new AutoTieringPolicyCapabilityDefinition();
    Map<String, List<String>> props = new HashMap<>();
    List<CapabilityInstance> capabilities = new ArrayList<>();
    String policyId = "Auto-Tier-Policy-1;
    props.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.POLICY_ID.name(), Arrays.asList(policyId));
    String provisioningType = StoragePool.AutoTieringPolicyProvisioningType.ThinlyProvisioned.name();
    props.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.PROVISIONING_TYPE.name(), Arrays.asList(provisioningType));
    CapabilityInstance capabilityInstance = new CapabilityInstance(capabilityDefinition.getId(), policyId, props);
    capabilities.add(capabilityInstance);
        
    // Add capabilities for other auto tiering policies supported by the storage pool.
    // Set the capabilities for the storage pool.
    pool.setCapabilities(capabilities);
        
    // Add the storage pool to the list of pools to be returned.
    storagePools.add(pool);
}

Auto Tiering Policies During Volume Provisioning

Creating a Block Virtual Pool 

Auto tiering policies discovered for SDK driver storage systems are used just like auto tiering policies for all other controller supported storage platforms. When creating a block virtual pool the user can select an auto tiering policy as illustrated in the screenshot below. Subsequently, when provisioning a volume using that virtual pool, the controller placement algorithm will select an appropriate storage pool that supports the auto tiering policy specified in the virtual pool.

 



NOTE: Currently, the HMTL file /views/arrays/BlockVirtualPools/edit.html shown below hard codes the storage system types for which the Auto-tiering Policy drop-down menu is shown. Eventually, this will be determined by examining the meta data for a given storage system type to determine if that system type supports auto tiering policies. For now, if your SDK storage driver needs auto-tiering policy support, its type must be added to this HTML page as shown in red highlight in the following code snippet. In this example, the storage system type for the SDK storage driver is "driversystem". Substitute this value with your storage system type.

portal/app/views/arrays/BlockVirtualPools/edit.html

<div ng-if="['driversystem','${models.StorageSystemTypes.VMAX}',${models.StorageSystemTypes.VNX_BLOCK}','${models.StorageSystemTypes.HITACHI}',
'${models.StorageSystemTypes.VNXe}','${models.StorageSystemTypes.UNITY}'].indexOf(vpool.systemType) > -1">
    <control-group v-field='vpool.autoTierPolicy'>
        select-one v-disabled="locked" auto-default options="autoTierPolicyOptions" empty-option="none"></select-one>
    </control-group>
</div>
Creating the Volume

When the controller invokes the createVolumes driver API,  the auto tiering policy selected for the virtual pool is passed to the driver. The auto tiering policy is passed in the StorageCapabilities parameter. This Class has an instance of CommonStorageCapabilities, which in turn manages a List<DataStorageServiceOption>. The DataStorageServiceOption class manages a List<CapabilityInstance>. The auto tiering policy is passed as a CapabilityInstance in this list as illustrated below.

 

private StorageCapabilities createStorageCapabilitiesForVolumeCreate(URI autoTieringPolicyURI) {
    StorageCapabilities storageCapabilities = new StorageCapabilities();
    if (!NullColumnValueGetter.isNullURI(autoTieringPolicyURI)) {
        AutoTieringPolicy autoTieringPolicy = dbClient.queryObject(AutoTieringPolicy.class, autoTieringPolicyURI);
        if (autoTieringPolicy == null) {
            throw DeviceControllerException.exceptions.objectNotFound(autoTieringPolicyURI);
        }
        AutoTieringPolicyCapabilityDefinition autoTieringCapabilityDefinition = new AutoTieringPolicyCapabilityDefinition();
        Map<String, List<String>> capabilityProperties = new HashMap<>();
        capabilityProperties.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.POLICY_ID.name(), Arrays.asList(autoTieringPolicy.getPolicyName()));
        capabilityProperties.put(AutoTieringPolicyCapabilityDefinition.PROPERTY_NAME.PROVISIONING_TYPE.name(), Arrays.asList(autoTieringPolicy.getProvisioningType()));
        CapabilityInstance capabilityInstance = new CapabilityInstance(autoTieringCapabilityDefinition.getId(), autoTieringPolicy.getPolicyName(), capabilityProperties);
        DataStorageServiceOption serviceOption = new DataStorageServiceOption(Arrays.asList(capabilityInstance));
        CommonStorageCapabilities commonCapabilities = new CommonStorageCapabilities();
        commonCapabilities.setDataStorage(Arrays.asList(serviceOption));
        storageCapabilities.setCommonCapabilitis(commonCapabilities);
    }
    return storageCapabilities;
}

 

CoprHD Services Available to Storage Driver

CoprHD provides LockManager and Registry services to storage drivers. These services are set at driver instantiation time.

LockManger

LockManager provides support for distributed inter-process locks. For example, if driver needs exclusive access to storage resource during provisioning operation and this is not provided by hardware locks, it can use LockManager to lock the resource. All other requests to lock the same resource from other threads running in CoprHD nodes will be blocked from resource access until the owner thread releases the lock for the resource.  LockManager provided by CoprHD implements re-entrant locks (the same thread may acquire the same lock multiple times). If CoprHD node failed, all locks held by its threads will be released automatically. Driver writer should take care to avoid deadlocks when process has to acquire multiple locks. Common way is to acquire locks in the same order.

Registry

Registry provides API for persistent distributed store of storage driver data. Please refer to Registry Javadoc for details.

Logging

log4j is available for storage drivers to use as a logging facility. By default driver log messages are added to controllersvc.log. This default configuration can be changed by editing controllersvc-log4j.properties.

Asynchronous Task Support

The controller and storage driver SDK now support asynchronous task execution for a limited number of operations. The storage driver APIs for which asynchronous execution is supported include the following:

 

Controller OperationStorage Driver SDK API
 Create Volume Clone createVolumeClone
 Create Group Clone createConsistencyGroupClone
 Expand Volume expandVolume
 Restore Volume Clone restoreFromClone
 Restore Group Clone restoreFromClone
 Restore Snapshot restoreSnapshot

 

These specific APIs are supported initially based on the needs of current and planned SDK storage drivers. These are the operations which currently take an extended period of time to complete execution and would benefit the most from asynchronous execution. Eventually, asynchronous execution should be supported for all storage driver operations.

Also note that the restore snapshot controller operation handles both single volume and consistency group snapshots.

In the Controller

Changes in the controller class ExternalBlockStorageDevice examine the TaskStatus of the DriverTask returned by the storage driver APIs associated with the operations that support asynchronous execution. If the status reflects a terminal state, it is assumed the operation was executed synchronously in the usual manner supported by the storage driver SDK. If, however, the status indicates a non-terminal state, the controller assumes that the task is being executed asynchronously. The terminal/non-terminal TaskStatus values are as follows:

 

DriverTask.TaskStatusTerminal/Non-Terminal
 QUEUED Non-Terminal
 PROVISIONING Non-Terminal
 READY Terminal
 FAILED Terminal
 PARTIALLY_FAILED Terminal
 WARNING Terminal
 ABORTED Terminal

 

When TaskStatus indicates asynchronous execution, the controller extracts the unique task id from the returned driver task, creates a Job derived class passing this unique task id, and queues the job for execution in the same exact manner that is done for monitoring the progress of asynchronous tasks for other controller supported platforms for example, asynchronous SMI-S operations. When the Job is polled, the controller will invoke the storage driver SDK API getTask passing the unique task id held by the job. This will return the DriverTask instance with the updated TaskStatus. The job will examine the status to see if execution has reached a terminal state, and behave accordingly. When the task execution eventually reaches a terminal state, the job will be removed from the queue, and the controller will execute the same exact behavior that is currently executed for synchronous execution of that particular operation, such as setting the native volume information for a newly created clone and calling the task completer.

There is a derived Job class for each supported asynchronous operation. All these classes extend the abstract ExternalDeviceJob class, which extends Job and contains the basic infrastructure for handling a poll request and provides abstract methods that are defined by derived classes for handling success and failure when the task reaches a terminal status. These classes are as follows:

  • CreateVolumeCloneExternalDeviceJob
  • CreateGroupCloneExternalDeviceJob
  • ExpandVolumeExternalDeviceJob
  • RestoreFromCloneExternalDeviceJob
  • RestoreFromGroupCloneExternalDeviceJob
  • RestoreFromSnapshotExternalDeviceJob


Create Volume Clone Example

Class ExternalBlockStorageDevice extends DefaultBlockStorageDevice
{
...
   
    @Override
    public void doCreateClone(StorageSystem storageSystem, URI volume, URI clone, Boolean createInactive, TaskCompleter taskCompleter) {
        ...
        DriverTask task = driver.createVolumeClone(Collections.unmodifiableList(driverClones), null);
        if (!isTaskInTerminalState(task.getStatus())) {
            // If the task is not in a terminal state and will be completed asynchronously
            // create a job to monitor the progress of the request and update the clone
            // volume and call the completer as appropriate based on the result of the request.
            CreateVolumeCloneExternalDeviceJob job = new CreateVolumeCloneExternalDeviceJob(
                storageSystem.getId(), clone, task.getTaskId(), taskCompleter);
            // Important: the job is queued to distributed queue — can be consumed by any controller node
            ControllerServiceImpl.enqueueJob(new QueueJob(job));
        } else {
            // Handle synchronous response in usual manner
        }
        ...
    }
   
...
}
abstract public class ExternalDeviceJob extends Job implements Serializable {
...
    /**
     * Constructor.
     *
     * @param storageSystemURI The URI of the external storage system on which the task is running.
     * @param driverTaskId The id of the task monitored by the job.
     * @param taskCompleter The task completer.
     */
    public ExternalDeviceJob(URI storageSystemURI, String driverTaskId, TaskCompleter taskCompleter) {
        _storageSystemURI = storageSystemURI;
        _driverTaskId = driverTaskId;
        _taskCompleter = taskCompleter;
    }
   
    public JobPollResult poll(JobContext jobContext, long trackingPeriodInMillis) {
        s_logger.info("Polled external device job for driver task {} on storage system {}", _driverTaskId, _storageSystemURI);
        DriverTask driverTask = null;
        DbClient dbClient = jobContext.getDbClient();
        try {
            // Update the job info.
            _pollResult.setJobName(_driverTaskId);
            _pollResult.setJobId(_driverTaskId);
            _pollResult.setJobPercentComplete(0);
           
            // Get the external storage system on which the driver task is running.
            StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, _storageSystemURI);
            if (storageSystem == null) {
                s_logger.error("Could not find external storage system {}", _storageSystemURI);
                throw ExternalDeviceException.exceptions.cantFindStorageSystemForDriverTask(
                        _storageSystemURI.toString(), _driverTaskId);
            }
            String systemType = storageSystem.getSystemType();
            // Get the external storage driver for the system on which the task is executing.
            BlockStorageDriver driver = ExternalBlockStorageDevice.getBlockStorageDriver(systemType);
            if (driver == null) {
                s_logger.error("Could not find storage driver for system type {}", systemType);
                throw ExternalDeviceException.exceptions.noDriverDefinedForDevice(systemType);
            }
           
            // Get the storage driver task.
            driverTask = driver.getTask(_driverTaskId);
            if (driverTask == null) {
                s_logger.error("Could not find storage driver task {} for storage system {}",
                        _driverTaskId, _storageSystemURI.toString());
                throw ExternalDeviceException.exceptions.cantFindDriverTaskOnSystem(
                        _driverTaskId, _storageSystemURI.toString());
            }
           
            TaskStatus taskStatus = driverTask.getStatus();
            String taskStatusMsg = driverTask.getMessage();
            if (isTaskSuccessful(taskStatus)) {
                // Completed successfully
                s_logger.info(String.format("Task %s completed successfully with status %s:%s",
                        _driverTaskId, taskStatus, taskStatusMsg));
                doTaskSucceeded(driverTask, dbClient);
                _pollResult.setJobPercentComplete(100);
                _status = JobStatus.SUCCESS;
            } else if (isTaskFailed(taskStatus)) {
                // Failed.
                s_logger.info(String.format("Task %s failed with status %s:%s", _driverTaskId,
                        taskStatus, taskStatusMsg));
                doTaskFailed(driverTask, dbClient);
                _pollResult.setJobPercentComplete(100);
                _errorDescription = taskStatusMsg;
                _status = JobStatus.FAILED;
            }
        ...
   
    }
   
....
}
Class CreateVolumeCloneExternalDeviceJob extends ExternalDeviceJob {
...
   
    @Override
    protected void doTaskSucceeded(DriverTask driverTask, DbClient dbClient) throws Exception {
        // Get the ViPR volume representing the clone.
        s_logger.info(String.format("Successfully created volume clone %s:%s", _volumeURI, driverTask.getMessage()));
        Volume volume = dbClient.queryObject(Volume.class, _volumeURI);
        if (volume == null) {
            s_logger.error(String.format("Failed to find volume %s", _volumeURI));
            throw DeviceControllerException.exceptions.objectNotFound(_volumeURI);
        }
        
        // Update the ViPR clone with the driver clone information.
        // Note that we know ViPR only allows creation of a single
        // in a given request.
        CreateVolumeCloneDriverTask createCloneDriverTask = (CreateVolumeCloneDriverTask) driverTask;
        List<VolumeClone> updatedClones = createCloneDriverTask.getClones();
        VolumeClone updatedClone = updatedClones.get(0);
        ExternalDeviceUtils.updateNewlyCreatedClone(volume, updatedClone, dbClient);
    }
   
    @Override
    protected void doTaskFailed(DriverTask driverTask, DbClient dbClient) throws Exception {
        s_logger.error(String.format("Failed to create volume clone %s:%s", _volumeURI, driverTask.getMessage()));
        Volume volume = dbClient.queryObject(Volume.class, _volumeURI);
        if (volume == null) {
            s_logger.error(String.format("Failed to find volume %s", _volumeURI));
        } else {
            volume.setInactive(true);
        dbClient.updateObject(volume);
        }
    }
   
...
}

In the Storage Driver

The external storage driver can now decide whether to implement the APIs listed above synchronously or asynchronously. If executed synchronously, the storage driver can simply return a DriverTask instance with the appropriate terminal TaskStatus, and if necessary, update the passed SDK model object reference(s) with any information required by that operation. For example, if creating a single volume clone, synchronous execution would update the passed VolumeClone instance with the native id of the created clone. This is the current expected storage driver behavior for synchronous operation execution and nothing changes in that regard. However, if the storage driver wants to execute the operation asynchronously, the storage driver must create and return a specific derived DriverTask class instance for that operation as shown in the table below. The TaskStatus of course should initially indicate one of the non-terminal states so that the controller treats the response asynchronously. Additionally, these classes manage the SDK model object reference(s) passed in the request. Instead of being updated by the storage driver directly, as in synchronous execution, it is required that the storage driver update the objects managed DriverTask derived class when the execution reaches a terminal state. In this way the task contains the updated information required by the controller. The driver has to store the task information in the Registry to be available for monitoring, through getTask(taskId) api, from all controller nodes.

 

Storage Driver SDK APIDriverTask Derived Class
 createVolumeClone CreateVolumeCloneDriverTask
 createConsistencyGroupClone CreateGroupCloneDriverTask
 expandVolume ExpandVolumeDriverTask
 restoreFromClone RestoreFromCloneDriverTask
 restoreSnapshot RestoreFromSnapshotDriverTask


Create Volume Clone Example

Note: This simulator implementation is for demo purpose only. It is not applicable for multi-node controller deployment. Real driver implementation has to store async task information in the distributed Registry (provided to drivers by SDK) in order for any controller node could access the task instance through driver implementation of getTask(taskId) SDK method.

Class StorageDriverSimulator extends DefaultStorageDriver implements BlockStorageDriver {
...
      
    @Override
    public DriverTask createVolumeClone(List<VolumeClone> clones, StorageCapabilities capabilities) {
        CreateVolumeCloneSimulatorOperation createCloneSimulatorOperation = new CreateVolumeCloneSimulatorOperation(clones);
        if (simulatorConfig.getSimulateAsynchronousResponses()) {
            // IMPORTANT: This operation class returns a CreateVolumeCloneDriverTask,
            // which is required for asynchronous execution. It is OK to return this
            // instance for synchronous execution as well, however, the contained clone
            // information is ignored. For synchronous execution it is expected the
            // passed instances are updated directly as shown. This sample could
            // just have returned a DriverTask instance for synchronous execution.
            DriverTask driverTask = createCloneSimulatorOperation.getDriverTask();
            taskOperationMap.put(driverTask.getTaskId(), createCloneSimulatorOperation);
            return driverTask;
        } else if (simulatorConfig.getSimulateFailures()) {
            String failMsg = createCloneSimulatorOperation.getFailureMessage();
            return createCloneSimulatorOperation.doFailure(failMsg);
        } else {
            createCloneSimulatorOperation.updateCloneInfo(clones);
            String successMsg = createCloneSimulatorOperation.getSuccessMessage(clones);
            return createCloneSimulatorOperation.doSuccess(successMsg);
        }
    }
   
    // Simply demonstrates that the driver must maintain a map of executing tasks
    // so that the controller can request a task by its unique id to get the
    // current status and see if it has completed. This simply simulates a
    // failed or succeeded asynchronous task after a certain number of requests
    // for that task.
    @Override
    public DriverTask getTask(String taskId) {
        if (!taskOperationMap.containsKey(taskId)) {
            _log.error("Invalid task Id {}", taskId);
            return null;
        }
       
        DriverSimulatorOperation taskOperation = taskOperationMap.get(taskId);
        if (taskOperation.getLookupCount() < simulatorConfig.getMaxAsynchronousLookups()) {           
            taskOperation.incrementLookupCount();
            _log.info("This is lookup {} for task {}", taskOperation.getLookupCount(), taskId);
        } else {
            taskOperationMap.remove(taskId);
            if (simulatorConfig.getSimulateFailures()) {
                _log.info("Simulating asynchronous failure for task {} of type {}", taskId, taskOperation.getType());
                String errorMsg = taskOperation.getFailureMessage();
                taskOperation.doFailure(errorMsg);
            } else {
                _log.info("Simulating asynchronous success for task {} of type {}", taskId, taskOperation.getType());
                taskOperation.updateOnAsynchronousSuccess();
                String successMsg = taskOperation.getSuccessMessage();
                taskOperation.doSuccess(successMsg);
            }
        }
       
        return taskOperation.getDriverTask();
    }
public class CreateVolumeCloneSimulatorOperation extends BaseDriverSimulatorOperation {
...
   
    /**
     * Constructor.
     *
     * @param clones A list of the clones to be created.
     */
    public CreateVolumeCloneSimulatorOperation(List<VolumeClone> clones) {
        super(OP_NAME);
        createDriverTask(clones);
    }
   
    /**
     * Update the clone information after successfully being created.
     *
     * @param clones A list of the clones to be updated.
     */
    public void updateCloneInfo(List<VolumeClone> clones) {
        for (VolumeClone clone : clones) {
            clone.setNativeId("clone-" + clone.getParentId() + clone.getDisplayName());
            clone.setWwn(String.format("%s%s", clone.getStorageSystemId(), clone.getNativeId()));
            clone.setReplicationState(VolumeClone.ReplicationState.SYNCHRONIZED);
            clone.setProvisionedCapacity(clone.getRequestedCapacity());
            clone.setAllocatedCapacity(clone.getRequestedCapacity());
            clone.setDeviceLabel(clone.getNativeId());
        }       
    }
   
    @Override
    public void updateOnAsynchronousSuccess() {
        CreateVolumeCloneDriverTask createCloneTask = (CreateVolumeCloneDriverTask)_task;
        updateCloneInfo(createCloneTask.getClones());
    }
   
    @Override
    public DriverTask doSuccess(String msg) {
        _task.setStatus(DriverTask.TaskStatus.READY);
        _log.info(msg);
        _task.setMessage(msg);
        return _task;
    }
   
    @Override
    public DriverTask doFailure(String msg) {
        _task.setStatus(DriverTask.TaskStatus.FAILED);
        _log.info(msg);
        _task.setMessage(msg);
        return _task;
    }
       
    /**
     * Create the create volume clone task that is returned by the request.
     *
     * @param clones A list of the clones to be created.
     */
    private void createDriverTask(List<VolumeClone> clones) {
        String taskId = String.format("%s+%s+%s", StorageDriverSimulator.DRIVER_NAME, OP_NAME, UUID.randomUUID().toString());
        _log.info("Creating task {} for operation of type {}", taskId, OP_NAME);
        _task = new CreateVolumeCloneDriverTask(taskId, clones);
        _task.setStatus(DriverTask.TaskStatus.PROVISIONING);
    }
   
...
}


How to Install driver into CoprHD instance

Build driver class jar file and copy it to CoprHD host. 

Before CoprHD can start to use the driver, you will need to configure driver and its storage system type and if required storage system provider type in CoprHD.

There are two parts in driver configuration. First, you need to configure driver in controller xml configuration files. This is described in the "Driver Configuration in Controller" below.

Second, you will need to create storage system type and if required storage system provider type in CoprHD UI. Storage system provider type is optional. It is required only when the driver manages storage systems through provider. This is described in the "How to Create StorageSystem Type in UI" below.

For the purpose of this description assume that there are two storage drivers. One driver manages storage systems with type abc_system through provider with type abc_provider. The second driver manages storage systems with type xyz_system directly, no provider required for xyz_system type. Both systems system types are block systems.

Driver Configuration in Controller

Driver installation steps:

a.

Add storage system type (and optionally storage provider type) managed by driver to driver-conf.xml in /opt/storageos/conf

--- add entry for the driver to  “storageSystemsMap” map. Key does not matter, but value should exactly match storage system type managed by driver.

--- Add storage system type to “blockSystems” or to "fileSystems" sets depending on type of storage managed by driver. If driver manages block and file storage, add storage system type to both sets.

--- Depending on storage management type, add storage system type either to “providerManaged” or to “directlyManaged” set.

If driver manages storage systems through storage provider, in addition do the following to add provider type to the configuration:

--- Add provider type to "storageProvidersMap". Key does not matter, but value should exactly match storage provider type managed by driver.

Example:

<bean id="storageDriverManager" class="com.emc.storageos.services.util.StorageDriverManager">
           <property name="storageSystemsMap">
              <map>
                 <entry key="ABC_System" value="abc_system"/>
                 <entry key="XYZ_System_Provider" value="xyz_system"/>

                ..........................................
              </map>
           </property>

           <property name="storageProvidersMap">
               <map>
                   <entry key="ABC_System_Provider" value="abc_provider"/>

                   ..........................................
               </map>
           </property>

           <!-- Set of block storage systems managed by drivers -->
           <property name="blockSystems">
               <set>
                   <value>abc_system</value> 

                  <value>xyz_system</value>
                   ......................................
               </set>
           </property>

           <!-- Set of file storage systems managed by drivers -->
           <property name="fileSystems">
               <set>

               </set>
           </property>

           <!-- Set of storage systems managed through providers (proxy management model) -->
           <property name="providerManaged">
               <set>
                         <value>abc_system</value>
                         ...................................

               </set>
           </property>

           <!-- Set of storage systems managed through direct connection from client -->
           <property name="directlyManaged">
               <set>
                   <value>xyz_system</value>
                   ...............................
               </set>
           </property>

       </bean>

 

b.

Find  controller-conf.xml under /opt/storageos/conf

Add block storage driver implementation bean to this xml. Add block driver implementation bean to “drivers” map in externalBlockStorageDevice bean. The key in the map should be storage system/provider type as defined in

driver-conf.xml . If block driver manages storage systems through provider, you need to map both storage system type and storage provider type to driver implementation bean in the map.

Example:

<bean id="externalBlockStorageDevice" class="com.emc.storageos.volumecontroller.impl.externaldevice.ExternalBlockStorageDevice">
        .......................................
        <!-- Block storage drivers -->
        <property name="drivers">
            <map>
                ............................................................
                <entry key="abc_system" value-ref="abcDriver"/>
                <entry key="abc_provider" value-ref="abcDriver"/>
                <entry key="xyz_system" value-ref="xyzDriver"/>
            </map>
        </property>
    </bean>


    <!-- Driver class for abc block storage driver -->
    <bean id="abcDriver" class="com.emc.storageos.driver.abc.AbcStorageDriver">
        </bean>
   

    <!-- Driver class for xyz block storage driver --> 
    <bean id="xyzDriver" class="com.emc.storageos.driver.xyz.XyzStorageDriver">
    </bean>

c.

Find  discovery-externaldevice-context.xml under /opt/storageos/conf  

Add discovery driver implementation bean to this xml. Add discovery driver implementation bean to “drivers” map in externaldevice bean. The key in the map should be storage system/provider type as defined in driver-conf.xml . 

If discovery driver manages storage systems through provider, you need to map both storage system type and storage provider type to discovery driver implementation bean in the map. 

The same driver class can provide block and discovery driver implementations.

Example:

<bean id="externaldevice" class="com.emc.storageos.volumecontroller.impl.plugins.ExternalDeviceCommunicationInterface">
        .......................................
        <!-- Discovery storage drivers -->
        <property name="drivers">
            <map> 

                .....................................................
                <entry key="abc_system" value-ref="abcDriver"/>
                <entry key="abc_provider" value-ref="abcDriver"/>
                <entry key="xyz_system" value-ref="xyzDriver"/>
            </map>
        </property>
    </bean>


    <!-- Driver class for abc block storage driver -->
    <bean id="abcDriver" class="com.emc.storageos.driver.abc.AbcStorageDriver">
        </bean>
   

    <!-- Driver class for xyz block storage driver --> 
    <bean id="xyzDriver" class="com.emc.storageos.driver.xyz.XyzStorageDriver">
    </bean>

d.

Copy driver jar to /opt/storageos/lib directory.

 

After you put driver jar file in /opt/storageos/lib directory, add driver jar file path to the controllersvc classpath.

Assume that you named the jar as abcdriver.jar.

In your install, find file /opt/storageos/bin/controllersvc .

Add  :${LIB_DIR}/ abcdriver.jar at the end of

export CLASSPATH=”….. line.

It should look as:

export CLASSPATH="….:${LIB_DIR}}/ abcdriver.jar”

 

e.

Restart CoprHD services. Check that controller service stared successfully.

How to Create StorageSystem Type in UI 

Assume your storage system type is abc_system (these should be the same value as you used  in “storageSystemsMap” map in driver-conf.xml to define storage system type)

This is what you need to do in UI:

1. On the left hand at the bottom, go to “System->Storage System Types”, click Add

Add storage system type

Add New Storage System Type

Enter all information needed for Storage System Type

Name:  abc_system (should be exactly as you used in xml config)

Display Name: ABC System (or whatever you prefer)

Is Provider: do not checkin

Type: block (should be block)

Supports SSL:

Port Number: 777

SSL Port Number:8777 (should specify both ports even if ssl is not supported)

Driver Class Name: Dummy.java (not used, so you can put any string here) 

 

Do “Save”.

 

2. This step is required only when driver manages storage systems through provider.

Assume your storage system provider type is abc_provider (these should be the same value as you used  in “storageProvidersMap” map in driver-conf.xml to define storage system provider type)

This is what you need to do in UI:

1. On the left hand at the bottom, go to “System->Storage System Types”, click Add

Add New Storage System Type

 

Enter all information needed for Storage System Type

Name:  abc_provider (should be exactly as you used in xml config)

Display Name: ABC Provider (or whatever you prefer)

Is Provider: checkin

Type: block (should be block)

Supports SSL:

Port Number: 777

SSL Port Number:8777 (should specify both ports even if ssl is not supported)

Driver Class Name: Dummy.java (not used, so you can put any string here) 

 

Do “Save”.

 

After that, you can proceed to create you storage system (or provider) in UI with configured above types and run discovery/scan the same way as you do for natively managed systems. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • No labels

3 Comments

  1. will the installation instructions provided above ensure that jar file/settings are not removed during ViPR C vApp reboot, or upgrade?

    thanks!

  2. Driver installation/deployment will be automated in the coming releases with required support for non-disruptive upgrade/reboot for installed drivers. The procedure above do not provide non-disruptive upgrade/reboot for installed drivers. After reboot, drivers have to be reinstalled.

  3. Stas has a good point.  Could we recommend saving off affected files to a directory in /data that will be persisted so at least they could copy them back over in the case of a reboot?