/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.job; import java.net.URI; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.CIMProperty; import javax.wbem.WBEMException; import javax.wbem.client.WBEMClient; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.coordinator.client.service.DistributedSemaphore; import com.emc.storageos.db.client.model.StorageSystem; import org.apache.curator.framework.recipes.locks.Lease; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.NameGenerator; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.smis.CIMPropertyFactory; import com.emc.storageos.volumecontroller.impl.smis.SmisConstants; /** * SmisJob implementation that handles the case of multi-volume create */ public class SmisCreateMultiVolumeJob extends SmisAbstractCreateVolumeJob { private static final Logger _log = LoggerFactory.getLogger(SmisCreateMultiVolumeJob.class); // These atomic references are for use in the volume rename step in processVolume private static final AtomicReference<NameGenerator> _nameGeneratorRef = new AtomicReference<NameGenerator>(); private static final AtomicReference<CIMPropertyFactory> _propertyFactoryRef = new AtomicReference<CIMPropertyFactory>(); // Executor used for short-lived task to update // the volume names in the background private static final Executor _executor = Executors.newCachedThreadPool(); private static final AtomicReference<DistributedSemaphore> _distributedLock = new AtomicReference<DistributedSemaphore>(); private static final AtomicReference<CoordinatorClient> _coordinator = new AtomicReference<CoordinatorClient>(); private static final int MAX_PERMITS = 10; public SmisCreateMultiVolumeJob(CIMObjectPath cimJob, URI storageSystem, URI storagePool, int count, TaskCompleter taskCompleter) { super(cimJob, storageSystem, storagePool, taskCompleter, String.format("Create%dVolumes", count)); // Keep a reference to these singletons _propertyFactoryRef.compareAndSet(null, (CIMPropertyFactory) ControllerServiceImpl.getBean("CIMPropertyFactory")); _nameGeneratorRef.compareAndSet(null, (NameGenerator) ControllerServiceImpl.getBean("defaultNameGenerator")); _coordinator.compareAndSet(null, (CoordinatorClient) ControllerServiceImpl.getBean("coordinator")); _distributedLock.compareAndSet(null, _coordinator.get(). getSemaphore(this.getClass().getSimpleName(), MAX_PERMITS)); } /** * Perform specific processing, based on the SMI-S Provider version. * * @param dbClient [in] - Client for reading/writing from/to database. * @param client [in] - WBEMClient for accessing SMI-S provider data * @param volume [in] - Reference to Bourne's Volume object * @param volumePath [in] - Name reference to the SMI-S side volume object */ @Override protected void specificProcessing(final StorageSystem storageSystem, final DbClient dbClient, final WBEMClient client, final Volume volume, CIMInstance volumeInstance, final CIMObjectPath volumePath) { if (storageSystem.getUsingSmis80()) { super.specificProcessing(storageSystem, dbClient, client, volume, volumeInstance, volumePath); } else { specificProcessingFor4x(dbClient, client, volume, volumePath); } } /** * Specific processing for SMI-S Provider 4.x. * * With this version we are limited to using CreateOrModifyElementFromStoragePool, so execute operations to rename * the volume name, which will run in the background. * * @param dbClient * @param client * @param volume * @param volumePath */ private void specificProcessingFor4x(final DbClient dbClient, final WBEMClient client, final Volume volume, final CIMObjectPath volumePath) { _executor.execute(new Runnable() { @Override public void run() { try { // Get the tenant name from the volume TenantOrg tenant = dbClient.queryObject(TenantOrg.class, volume.getTenant().getURI()); String tenantName = tenant.getLabel(); // Generate the name, then modify the volume instance // that was successfully created if (_nameGeneratorRef.get() == null) { _nameGeneratorRef.compareAndSet(null, (NameGenerator) ControllerServiceImpl.getBean("defaultNameGenerator")); } String generatedName = _nameGeneratorRef.get().generate(tenantName, volume.getLabel(), volume.getId().toString(), '-', SmisConstants.MAX_VOLUME_NAME_LENGTH); changeVolumeName(dbClient, client, volumePath, volume, generatedName); } catch (DatabaseException e) { _log.error("Encountered an error while trying to set the volume name", e); } catch (Exception e) { _log.error("Encountered an error while trying to set the volume name", e); } } }); } /** * Method will modify the name of a given volume to a generate name. * * @param dbClient [in] - Client instance for reading/writing from/to DB * @param client [in] - WBEMClient used for reading/writing from/to SMI-S * @param volumePath [in] - CIMObjectPath referencing the volume * @param volume [in] - Volume object */ private void changeVolumeName(DbClient dbClient, WBEMClient client, CIMObjectPath volumePath, Volume volume, String name) { Lease lease = null; try { _log.info(String.format("Attempting to modify volume %s to %s", volumePath.toString(), name)); if (_propertyFactoryRef.get() == null) { _propertyFactoryRef.compareAndSet(null, (CIMPropertyFactory) ControllerServiceImpl.getBean("CIMPropertyFactory")); } CIMInstance toUpdate = new CIMInstance(volumePath, new CIMProperty[] { _propertyFactoryRef.get().string(SmisConstants.CP_ELEMENT_NAME, name) } ); if (_distributedLock.get() == null) { if (_coordinator.get() == null) { _coordinator.compareAndSet(null, (CoordinatorClient) ControllerServiceImpl.getBean("coordinator")); } _distributedLock.compareAndSet(null, _coordinator.get(). getSemaphore(this.getClass().getSimpleName(), MAX_PERMITS)); } lease = _distributedLock.get().acquireLease(); client.modifyInstance(toUpdate, SmisConstants.PS_ELEMENT_NAME); _distributedLock.get().returnLease(lease); lease = null; volume.setDeviceLabel(name); dbClient.persistObject(volume); _log.info(String.format("Volume name has been modified to %s", name)); } catch (WBEMException e) { _log.error("Encountered an error while trying to set the volume name", e); } catch (DatabaseException e) { _log.error("Encountered an error while trying to set the volume name", e); } catch (Exception e) { _log.error("Encountered an error while trying to set the volume name", e); } finally { if (lease != null) { try { _distributedLock.get().returnLease(lease); } catch (Exception e) { _log.error("Exception when trying to return lease", e); } } } } }