/** * Validate the Target Volume VirtualArray with the Source Volume VPool VirtualArray. * * @param type * @param unManagedVolume * @param virtualArray */ private void validateTargetVolumeVpoolWithSourceVolume( UnManagedVolume unManagedVolume, VirtualArray virtualArray) { String sourceUnManagedVolumeId = PropertySetterUtil.extractValueFromStringSet( SupportedVolumeInformation.REMOTE_MIRROR_SOURCE_VOLUME.toString(), unManagedVolume.getVolumeInformation()); String sourceVolumeId = sourceUnManagedVolumeId.replace( VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME); List<URI> sourceUris = _dbClient.queryByConstraint( AlternateIdConstraint.Factory.getVolumeNativeGuidConstraint(sourceVolumeId)); if (sourceUris.isEmpty()) { _logger.info( "Source {} Not found for target {}", sourceVolumeId, unManagedVolume.getNativeGuid()); } else { // if source volume is ingested, then Volume sourceVolume = _dbClient.queryObject(Volume.class, sourceUris.get(0)); // check whether the source Volume's VPool is actually having this // target Volume's varray // specified as remote VirtualPool sourceVPool = _dbClient.queryObject(VirtualPool.class, sourceVolume.getVirtualPool()); Map<URI, VpoolRemoteCopyProtectionSettings> settings = sourceVPool.getRemoteProtectionSettings(sourceVPool, _dbClient); if (null == settings || settings.isEmpty() || !settings.containsKey(virtualArray.getId())) { _logger.info( "Target Volume's VArray {} is not matching already ingested source volume virtual pool's remote VArray ", virtualArray.getId()); throw IngestionException.exceptions.unmanagedSRDFTargetVolumeVArrayMismatch( unManagedVolume.getLabel(), sourceVolume.getVirtualArray().toString()); } } }
/** * Allocate, initialize and persist state of the Bucket being created. * * @param param * @param project * @param tenantOrg * @param neighborhood * @param vpool * @param flags * @param placement * @return */ private Bucket prepareBucket( BucketParam param, Project project, TenantOrg tenantOrg, VirtualArray neighborhood, VirtualPool vpool, DataObject.Flag[] flags, BucketRecommendation placement) { _log.debug("Preparing Bucket creation for Param : {}", param); StoragePool pool = null; Bucket bucket = new Bucket(); bucket.setId(URIUtil.createId(Bucket.class)); bucket.setLabel(param.getLabel().replaceAll(SPECIAL_CHAR_REGEX, "")); bucket.setHardQuota(SizeUtil.translateSize(param.getHardQuota())); bucket.setSoftQuota(SizeUtil.translateSize(param.getSoftQuota())); bucket.setRetention(Integer.valueOf(param.getRetention())); bucket.setOwner(getOwner(param.getOwner())); bucket.setNamespace(tenantOrg.getNamespace()); bucket.setVirtualPool(param.getVpool()); if (project != null) { bucket.setProject(new NamedURI(project.getId(), bucket.getLabel())); } bucket.setTenant(new NamedURI(tenantOrg.getId(), param.getLabel())); bucket.setVirtualArray(neighborhood.getId()); if (null != placement.getSourceStoragePool()) { pool = _dbClient.queryObject(StoragePool.class, placement.getSourceStoragePool()); if (null != pool) { bucket.setProtocol(new StringSet()); bucket .getProtocol() .addAll( VirtualPoolUtil.getMatchingProtocols(vpool.getProtocols(), pool.getProtocols())); } } bucket.setStorageDevice(placement.getSourceStorageSystem()); bucket.setPool(placement.getSourceStoragePool()); bucket.setOpStatus(new OpStatusMap()); // Bucket name to be used at Storage System String bucketName = project.getLabel() + UNDER_SCORE + param.getLabel(); bucket.setName(bucketName.replaceAll(SPECIAL_CHAR_REGEX, "")); // Update Bucket path StringBuilder bucketPath = new StringBuilder(); bucketPath .append(tenantOrg.getNamespace()) .append(SLASH) .append(project.getLabel()) .append(SLASH) .append(param.getLabel()); bucket.setPath(bucketPath.toString()); if (flags != null) { bucket.addInternalFlags(flags); } _dbClient.createObject(bucket); return bucket; }
@Override public <T extends BlockObject> T ingestBlockObjects( List<URI> systemCache, List<URI> poolCache, StorageSystem system, UnManagedVolume unManagedVolume, VirtualPool vPool, VirtualArray virtualArray, Project project, TenantOrg tenant, List<UnManagedVolume> unManagedVolumesToBeDeleted, Map<String, BlockObject> createdObjectMap, Map<String, List<DataObject>> updatedObjectMap, boolean unManagedVolumeExported, Class<T> clazz, Map<String, StringBuffer> taskStatusMap) throws IngestionException { // For VPLEX volumes, verify that it is OK to ingest the unmanaged // volume into the requested virtual array. long timeRightNow = new Date().getTime(); if (timeRightNow > (cacheLastRefreshed + CACHE_TIMEOUT)) { _logger.debug("clearing vplex ingestion api info cache"); clusterIdToNameMap.clear(); varrayToClusterIdMap.clear(); cacheLastRefreshed = timeRightNow; } if (!VolumeIngestionUtil.isValidVarrayForUnmanagedVolume( unManagedVolume, virtualArray.getId(), clusterIdToNameMap, varrayToClusterIdMap, _dbClient)) { _logger.warn( "UnManaged Volume {} cannot be ingested into the requested varray. Skipping Ingestion.", unManagedVolume.getLabel()); throw IngestionException.exceptions.varrayIsInvalidForVplexVolume( virtualArray.getLabel(), unManagedVolume.getLabel()); } return super.ingestBlockObjects( systemCache, poolCache, system, unManagedVolume, vPool, virtualArray, project, tenant, unManagedVolumesToBeDeleted, createdObjectMap, updatedObjectMap, unManagedVolumeExported, clazz, taskStatusMap); }
@Override protected void checkUnManagedVolumeAddedToCG( UnManagedVolume unManagedVolume, VirtualArray virtualArray, TenantOrg tenant, Project project, VirtualPool vPool) { if (VolumeIngestionUtil.checkUnManagedResourceAddedToConsistencyGroup(unManagedVolume)) { URI consistencyGroupUri = VolumeIngestionUtil.getVplexConsistencyGroup( unManagedVolume, vPool, project.getId(), tenant.getId(), virtualArray.getId(), _dbClient); if (null == consistencyGroupUri) { _logger.warn( "A Consistency Group for the VPLEX volume could not be determined. Skipping Ingestion."); throw IngestionException.exceptions .unmanagedVolumeVplexConsistencyGroupCouldNotBeIdentified(unManagedVolume.getLabel()); } } }
private TaskResourceRep initiateBucketCreation( BucketParam param, Project project, TenantOrg tenant, DataObject.Flag[] flags) throws InternalException { ArgValidator.checkFieldUriType(param.getVpool(), VirtualPool.class, "vpool"); ArgValidator.checkFieldUriType(param.getVarray(), VirtualArray.class, "varray"); Long softQuota = SizeUtil.translateSize(param.getSoftQuota()); Long hardQuota = SizeUtil.translateSize(param.getHardQuota()); Integer retention = Integer.valueOf(param.getRetention()); // Hard Quota should be more than SoftQuota verifyQuotaValues(softQuota, hardQuota, param.getLabel()); // check varray VirtualArray neighborhood = _dbClient.queryObject(VirtualArray.class, param.getVarray()); ArgValidator.checkEntity(neighborhood, param.getVarray(), false); _permissionsHelper.checkTenantHasAccessToVirtualArray(tenant.getId(), neighborhood); // check vpool reference VirtualPool cos = _dbClient.queryObject(VirtualPool.class, param.getVpool()); _permissionsHelper.checkTenantHasAccessToVirtualPool(tenant.getId(), cos); ArgValidator.checkEntity(cos, param.getVpool(), false); if (!VirtualPool.Type.object.name().equals(cos.getType())) { throw APIException.badRequests.virtualPoolNotForObjectStorage(VirtualPool.Type.object.name()); } // verify retention. Its validated only if Retention is configured. if (retention != 0 && cos.getMaxRetention() != 0 && retention > cos.getMaxRetention()) { throw APIException.badRequests.insufficientRetentionForVirtualPool(cos.getLabel(), "bucket"); } VirtualPoolCapabilityValuesWrapper capabilities = new VirtualPoolCapabilityValuesWrapper(); capabilities.put(VirtualPoolCapabilityValuesWrapper.RESOURCE_COUNT, Integer.valueOf(1)); capabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.FALSE); List<BucketRecommendation> placement = _bucketScheduler.placeBucket(neighborhood, cos, capabilities); if (placement.isEmpty()) { throw APIException.badRequests.noMatchingStoragePoolsForVpoolAndVarray( cos.getId(), neighborhood.getId()); } // Randomly select a recommended pool Collections.shuffle(placement); BucketRecommendation recommendation = placement.get(0); String task = UUID.randomUUID().toString(); Bucket bucket = prepareBucket(param, project, tenant, neighborhood, cos, flags, recommendation); _log.info( String.format( "createBucket --- Bucket: %1$s, StoragePool: %2$s, StorageSystem: %3$s", bucket.getId(), recommendation.getSourceStoragePool(), recommendation.getSourceStorageSystem())); Operation op = _dbClient.createTaskOpStatus( Bucket.class, bucket.getId(), task, ResourceOperationTypeEnum.CREATE_BUCKET); op.setDescription("Bucket Create"); // Controller invocation StorageSystem system = _dbClient.queryObject(StorageSystem.class, recommendation.getSourceStorageSystem()); ObjectController controller = getController(ObjectController.class, system.getSystemType()); controller.createBucket( recommendation.getSourceStorageSystem(), recommendation.getSourceStoragePool(), bucket.getId(), bucket.getName(), bucket.getNamespace(), bucket.getRetention(), bucket.getHardQuota(), bucket.getSoftQuota(), bucket.getOwner(), task); auditOp( OperationTypeEnum.CREATE_BUCKET, true, AuditLogManager.AUDITOP_BEGIN, param.getLabel(), param.getHardQuota(), neighborhood.getId().toString(), project == null ? null : project.getId().toString()); return toTask(bucket, task, op); }
/** {@inheritDoc} */ @Override public TaskList create( List<BlockObject> fcSourceObjList, VirtualArray varray, String name, boolean createInactive, int count, String taskId) { // Populate the descriptors list with all volumes required // to create the VPLEX volume copies. int sourceCounter = 0; URI vplexSrcSystemId = null; TaskList taskList = new TaskList(); List<Volume> vplexCopyVolumes = new ArrayList<Volume>(); List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>(); List<BlockObject> sortedSourceObjectList = sortFullCopySourceList(fcSourceObjList); for (BlockObject fcSourceObj : sortedSourceObjectList) { URI fcSourceURI = fcSourceObj.getId(); if (URIUtil.isType(fcSourceURI, BlockSnapshot.class)) { // Full copy of snapshots is not supported for VPLEX. return super.create(sortedSourceObjectList, varray, name, createInactive, count, taskId); } Volume vplexSrcVolume = (Volume) fcSourceObj; String copyName = name + (sortedSourceObjectList.size() > 1 ? "-" + ++sourceCounter : ""); // Create a volume descriptor for the source VPLEX volume being copied. // and add it to the descriptors list. Be sure to identify this VPLEX // volume as the source volume being copied. vplexSrcSystemId = fcSourceObj.getStorageController(); VolumeDescriptor vplexSrcVolumeDescr = new VolumeDescriptor( VolumeDescriptor.Type.VPLEX_VIRT_VOLUME, vplexSrcSystemId, fcSourceURI, null, null); Map<String, Object> descrParams = new HashMap<String, Object>(); descrParams.put(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID, Boolean.TRUE); vplexSrcVolumeDescr.setParameters(descrParams); volumeDescriptors.add(vplexSrcVolumeDescr); // Get some info about the VPLEX volume being copied and its storage // system. Project vplexSrcProject = BlockFullCopyUtils.queryFullCopySourceProject(fcSourceObj, _dbClient); StorageSystem vplexSrcSystem = _dbClient.queryObject(StorageSystem.class, vplexSrcSystemId); Project vplexSystemProject = VPlexBlockServiceApiImpl.getVplexProject(vplexSrcSystem, _dbClient, _tenantsService); // For the VPLEX volume being copied, determine which of the associated // backend volumes is the primary and, for distributed volumes, which // is the HA volume. The primary volume will be natively copied and we // we need to place and prepare a volume to hold the copy. This copy // will be the primary backend volume for the VPLEX volume copy. For // a distributed virtual volume, we will need to place and prepare // a volume to hold the HA volume of the VPLEX volume copy. Volume vplexSrcPrimaryVolume = null; Volume vplexSrcHAVolume = null; StringSet assocVolumeURIs = vplexSrcVolume.getAssociatedVolumes(); Iterator<String> assocVolumeURIsIter = assocVolumeURIs.iterator(); while (assocVolumeURIsIter.hasNext()) { URI assocVolumeURI = URI.create(assocVolumeURIsIter.next()); Volume assocVolume = _dbClient.queryObject(Volume.class, assocVolumeURI); if (assocVolume.getVirtualArray().toString().equals(varray.getId().toString())) { vplexSrcPrimaryVolume = assocVolume; } else { vplexSrcHAVolume = assocVolume; } } // Get the capabilities VirtualPool vpool = BlockFullCopyUtils.queryFullCopySourceVPool(fcSourceObj, _dbClient); VirtualPoolCapabilityValuesWrapper capabilities = getCapabilitiesForFullCopyCreate(fcSourceObj, vpool, count); // Get the number of copies to create and the size of the volumes. // Note that for the size, we must use the actual provisioned size // of the source side backend volume. The size passed in the // capabilities will be the size of the VPLEX volume. When the // source side backend volume for the copy is provisioned, you // might not get that actual size. On VMAX, the size will be slightly // larger while for VNX the size will be exactly what is requested. // So, if the source side is a VMAX, the source side for the copy // will be slightly larger than the size in the capabilities. If the HA // side is VNX and we use the size in the capabilities, then you will // get exactly that size for the HA backend volume. As a result, source // side backend volume for the copy will be slightly larger than the // HA side. Now the way a VPLEX copy is made is it uses native full // copy to create a native full copy of the source side backend // volume. It then provisions the HA side volume. The new source side // backend copy is then imported into VPLEX in the same way as is done // for a vpool change that imports a volume to VPLEX. This code in the // VPLEX controller creates a local VPLEX volume using the source side // copy and for a distributed volume it then attaches as a remote // mirror the HA backend volume that is provisioned. If the HA volume // is slightly smaller, then this will fail on the VPLEX. So, we must // ensure that HA side volume is big enough by using the provisioned // capacity of the source side backend volume of the VPLEX volume being // copied. long size = vplexSrcPrimaryVolume.getProvisionedCapacity(); // Place and prepare a volume for each copy to serve as a native // copy of a VPLEX backend volume. The VPLEX backend volume that // is copied is the backend volume in the same virtual array as the // VPLEX volume i.e, the primary backend volume. Create // descriptors for these prepared volumes and add them to the list. List<Volume> vplexCopyPrimaryVolumes = prepareFullCopyPrimaryVolumes( copyName, count, vplexSrcPrimaryVolume, capabilities, volumeDescriptors); // If the VPLEX volume being copied is distributed, then the VPLEX // HA volume should be non-null. We use the VPLEX scheduler to place // and then prepare volumes for the HA volumes of the VPLEX volume // copies. This should be done in the same manner as is done for the // import volume routine. This is because to form the VPLEX volume // copy we import the copy of the primary backend volume. List<Volume> vplexCopyHAVolumes = new ArrayList<Volume>(); if (vplexSrcHAVolume != null) { vplexCopyHAVolumes.addAll( prepareFullCopyHAVolumes( copyName, count, size, vplexSrcSystem, vplexSystemProject, varray, vplexSrcHAVolume, taskId, volumeDescriptors)); } // For each copy to be created, place and prepare a volume for the // primary backend volume copy. When copying a distributed VPLEX // volume, we also must place and prepare a volume for the HA // backend volume copy. Lastly, we must prepare a volume for the // VPLEX volume copy. Create descriptors for these prepared volumes // and add them to the volume descriptors list. for (int i = 0; i < count; i++) { // Prepare a new VPLEX volume for each copy. Volume vplexCopyPrimaryVolume = vplexCopyPrimaryVolumes.get(i); Volume vplexCopyHAVolume = null; if (vplexCopyHAVolumes.size() != 0) { vplexCopyHAVolume = vplexCopyHAVolumes.get(i); } Volume vplexCopyVolume = prepareFullCopyVPlexVolume( copyName, count, i, size, vplexSrcVolume, vplexSrcProject, varray, vpool, vplexSrcSystemId, vplexCopyPrimaryVolume, vplexCopyHAVolume, taskId, volumeDescriptors); vplexCopyVolumes.add(vplexCopyVolume); // Create task for each copy. Operation op = vplexCopyVolume.getOpStatus().get(taskId); TaskResourceRep task = toTask(vplexCopyVolume, taskId, op); taskList.getTaskList().add(task); } } // Invoke the VPLEX controller to create the copies. try { s_logger.info("Getting VPlex controller {}.", taskId); VPlexController controller = getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString()); // TBD controller needs to be updated to handle CGs. controller.createFullCopy(vplexSrcSystemId, volumeDescriptors, taskId); s_logger.info("Successfully invoked controller."); } catch (InternalException e) { s_logger.error("Controller error", e); // Update the status for the VPLEX volume copies and their // corresponding tasks. for (Volume vplexCopyVolume : vplexCopyVolumes) { Operation op = vplexCopyVolume.getOpStatus().get(taskId); if (op != null) { op.error(e); vplexCopyVolume.getOpStatus().updateTaskStatus(taskId, op); _dbClient.persistObject(vplexCopyVolume); for (TaskResourceRep task : taskList.getTaskList()) { if (task.getResource().getId().equals(vplexCopyVolume.getId())) { task.setState(op.getStatus()); task.setMessage(op.getMessage()); break; } } } } // Mark all volumes inactive, except for the VPLEX volume // we were trying to copy. for (VolumeDescriptor descriptor : volumeDescriptors) { if (descriptor.getParameters().get(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID) == null) { Volume volume = _dbClient.queryObject(Volume.class, descriptor.getVolumeURI()); volume.setInactive(true); _dbClient.persistObject(volume); } } } return taskList; }