/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.snapshot; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.ws.rs.core.UriInfo; import com.emc.storageos.api.service.impl.resource.ArgValidator; import com.emc.storageos.api.service.impl.resource.utils.BlockServiceUtils; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.Constraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.BlockSnapshotSession; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.svcs.errorhandling.resources.APIException; /** * */ public class BlockSnapshotSessionUtils { /** * Returns the volume or block snapshot instance for the passed URI. * * @param sourceURI The URI for the Volume or BlockSnapshot instance. * @param uriInfo A reference to the URI information. * @param checkAssociatedVolumes check if the passed source is an associated volume for another volume. * @param dbClient A reference to a database client. * * @return A reference to the block object. */ public static BlockObject querySnapshotSessionSource(URI sourceURI, UriInfo uriInfo, boolean checkAssociatedVolumes, DbClient dbClient) { ArgValidator.checkUri(sourceURI); if ((!URIUtil.isType(sourceURI, Volume.class)) && (!URIUtil.isType(sourceURI, BlockSnapshot.class))) { throw APIException.badRequests.invalidSnapshotSessionSource(sourceURI.toString()); } BlockObject sourceObj = BlockObject.fetch(dbClient, sourceURI); ArgValidator.checkEntity(sourceObj, sourceURI, BlockServiceUtils.isIdEmbeddedInURL(sourceURI, uriInfo), true); // This essentially checks if the passed snapshot session source is // a backend volume for a VPLEX volume, in which case the VPLEX volume // is returned. if (URIUtil.isType(sourceURI, Volume.class) && (checkAssociatedVolumes)) { List<Volume> volumes = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, Volume.class, AlternateIdConstraint.Factory.getVolumeByAssociatedVolumesConstraint(sourceURI.toString())); if (!volumes.isEmpty()) { sourceObj = volumes.get(0); } } return sourceObj; } /** * Returns the project for the snapshot session source. * * @param sourceObj A reference to the Volume or BlockSnapshot instance. * @param dbClient A reference to a database client. * * @return A reference to the project for the snapshot session source. */ public static Project querySnapshotSessionSourceProject(BlockObject sourceObj, DbClient dbClient) { URI sourceURI = sourceObj.getId(); URI projectURI = null; if (URIUtil.isType(sourceURI, Volume.class)) { projectURI = ((Volume) sourceObj).getProject().getURI(); } else if (URIUtil.isType(sourceURI, BlockSnapshot.class)) { projectURI = ((BlockSnapshot) sourceObj).getProject().getURI(); } if (projectURI == null) { throw APIException.badRequests.invalidSnapshotSessionSource(sourceURI.toString()); } Project project = dbClient.queryObject(Project.class, projectURI); return project; } /** * Returns the vpool for the snapshot session source. * * TBD Future - Tried to write this to be generic such that the source * may be a Volume or BlockSnapshot even though we don't currently support * creating a snapshot session of a BlockSnapshot. However, if we do * support this then we can't assume that parent of the passed source is * a Volume. We may need to do something recursively to get back the * originating source volume for these cascaded snapshots. * * @param sourceObj A reference to the Volume or BlockSnapshot instance. * @param dbClient A reference to a database client. * * @return A reference to the vpool for the snapshot session source. */ public static VirtualPool querySnapshotSessionSourceVPool(BlockObject sourceObj, DbClient dbClient) { URI sourceURI = sourceObj.getId(); URI vpoolURI = null; if (URIUtil.isType(sourceURI, Volume.class)) { vpoolURI = ((Volume) sourceObj).getVirtualPool(); } else if (URIUtil.isType(sourceURI, BlockSnapshot.class)) { URI parentURI = ((BlockSnapshot) sourceObj).getParent().getURI(); // It may be possible that the source for the snapshot // session is a backend source volume for a VPLEX volume // when we support creating snapshot sessions for VPLEX // volumes backed by storage that supports snapshot sessions. // In this case, we want the VPLEX volume vpool. URIQueryResultList results = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory.getVolumeByAssociatedVolumesConstraint(parentURI.toString()), results); Iterator<URI> resultsIter = results.iterator(); if (resultsIter.hasNext()) { parentURI = resultsIter.next(); } Volume parentVolume = dbClient.queryObject(Volume.class, parentURI); vpoolURI = parentVolume.getVirtualPool(); } if (vpoolURI == null) { throw APIException.badRequests.invalidSnapshotSessionSource(sourceURI.toString()); } VirtualPool vpool = dbClient.queryObject(VirtualPool.class, vpoolURI); return vpool; } /** * Validates and returns the BlockSnapshotSession instance with the passed URI. * * @param sourceURI The URI for a BlockSnapshotSession instance. * @param uriInfo A reference to the URI information. * @param dbClient A reference to a database client. * @param checkInactive true to check if the snapshot session is inactive. * * @return A reference to the BlockSnapshotSession instance. */ public static BlockSnapshotSession querySnapshotSession(URI snapSessionURI, UriInfo uriInfo, DbClient dbClient, boolean checkInactive) { ArgValidator.checkUri(snapSessionURI); BlockSnapshotSession snapSession = dbClient.queryObject(BlockSnapshotSession.class, snapSessionURI); ArgValidator.checkEntity(snapSession, snapSessionURI, BlockServiceUtils.isIdEmbeddedInURL(snapSessionURI, uriInfo), checkInactive); return snapSession; } /** * Validate that the passed targets represented by the BlockSnapshot instances * with the passed URIs are linked to the passed BlockSnapshotSession. * * @param snapSession A reference to a BlockSnapshotSession. * @param snapshotURIs The URIs of the BlockSnapshot instances to verify. * @param uriInfo A reference to the URI information. * @param dbClient A reference to a database client. * * @return A list of BlockSnapshot instances representing the session targets. */ public static List<BlockSnapshot> validateSnapshotSessionTargets(BlockSnapshotSession snapSession, Set<URI> snapshotURIs, UriInfo uriInfo, DbClient dbClient) { StringSet sessionTargets = snapSession.getLinkedTargets(); if ((sessionTargets == null) || (sessionTargets.isEmpty())) { // The snapshot session does not have any targets. throw APIException.badRequests.snapshotSessionDoesNotHaveAnyTargets(snapSession.getId().toString()); } List<BlockSnapshot> snapshots = new ArrayList<BlockSnapshot>(); Iterator<URI> snapshotURIsIter = snapshotURIs.iterator(); while (snapshotURIsIter.hasNext()) { // Snapshot session targets are represented by BlockSnapshot instances in ViPR. URI snapshotURI = snapshotURIsIter.next(); BlockSnapshot snapshot = validateSnapshot(snapshotURI, uriInfo, dbClient); String snapshotId = snapshotURI.toString(); if (!sessionTargets.contains(snapshotId)) { // The target is not linked to the snapshot session. throw APIException.badRequests.targetIsNotLinkedToSnapshotSession(snapshotId, snapSession.getId().toString()); } snapshots.add(snapshot); } return snapshots; } /** * Validate the BlockSnapshot instance with the passed URI. * * @param snapshotURI The URI of the BlockSnapshot instance to verify. * @param uriInfo A reference to the URI information. * @param dbClient A reference to a database client. * * @return A reference to the BlockSnapshot instance. */ public static BlockSnapshot validateSnapshot(URI snapshotURI, UriInfo uriInfo, DbClient dbClient) { ArgValidator.checkUri(snapshotURI); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotURI); ArgValidator.checkEntity(snapshot, snapshotURI, BlockServiceUtils.isIdEmbeddedInURL(snapshotURI, uriInfo), true); return snapshot; } /** * Return the BlockSnapshotSession associated with the given BlockSnapshot. * * @param snapshot BlockSnapshot. * @param dbClient Database client. * @return BlockSnapshotSession, or null if snapshot is not a linked target. */ public static BlockSnapshotSession getLinkedTargetSnapshotSession(BlockSnapshot snapshot, DbClient dbClient) { List<BlockSnapshotSession> sessions = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, BlockSnapshotSession.class, ContainmentConstraint.Factory.getLinkedTargetSnapshotSessionConstraint(snapshot.getId())); if (!sessions.isEmpty()) { return sessions.get(0); } return null; } /** * Determines if the passed volume has a snapshot session. * * @param volume A reference to the volume. * @param dbClient A reference to a database client. * * @return true if the volume has an active session, false otherwise. */ public static boolean volumeHasSnapshotSession(Volume volume, DbClient dbClient) { Constraint constraint = null; URI cgURI = volume.getConsistencyGroup(); if (NullColumnValueGetter.isNullURI(cgURI)) { constraint = ContainmentConstraint.Factory.getParentSnapshotSessionConstraint(volume.getId()); } else { constraint = ContainmentConstraint.Factory.getBlockSnapshotSessionByConsistencyGroup(cgURI); } List<BlockSnapshotSession> snapSessions = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, BlockSnapshotSession.class, constraint); return !snapSessions.isEmpty(); } }