@@ -198,10 +198,12 @@ private boolean allVolumesOnOntapManagedStorage(long vmId) {
198198 * Creates a per-volume disk snapshot as part of a VM snapshot operation.
199199 *
200200 * <p>Overrides the parent to ensure {@code quiescevm} is always {@code false}
201- * in the per-volume snapshot payload. ONTAP handles quiescing at the VM level
202- * via QEMU guest agent freeze/thaw in {@link #takeVMSnapshot}, so the
203- * individual volume snapshot must not request quiescing again. Without this
204- * override, {@link org.apache.cloudstack.storage.snapshot.DefaultSnapshotStrategy#takeSnapshot}
201+ * in the per-volume snapshot payload. When quiescing is requested, ONTAP handles
202+ * it at the VM level via QEMU guest agent freeze/thaw in {@link #takeVMSnapshot},
203+ * so the individual volume snapshot must not request quiescing again. Without this
204+ * override, {@link org.apache.cloudstack.storage.snapshot.StorageSystemSnapshotStrategy#takeSnapshot}
205+ * would attempt a second freeze/thaw for each volume, and
206+ * {@link org.apache.cloudstack.storage.snapshot.DefaultSnapshotStrategy#takeSnapshot}
205207 * would reject the request with "can't handle quiescevm equal true for volume snapshot"
206208 * when the user selects the quiesce option in the UI.</p>
207209 */
@@ -226,9 +228,11 @@ protected SnapshotInfo createDiskSnapshot(VMSnapshot vmSnapshot, List<SnapshotIn
226228 * Takes a VM-level snapshot by freezing the VM, creating per-volume snapshots
227229 * on ONTAP storage (file clones), and then thawing the VM.
228230 *
229- * <p>The quiesce option is always {@code true} for ONTAP to ensure filesystem
230- * consistency across all volumes. The QEMU guest agent must be installed and
231- * running inside the guest VM.</p>
231+ * <p>If the user requests quiescing ({@code quiescevm=true}), the QEMU guest
232+ * agent is used to freeze/thaw the VM file systems for application-consistent
233+ * snapshots. If {@code quiescevm=false}, the snapshots are crash-consistent
234+ * only. The QEMU guest agent must be installed and running inside the guest VM
235+ * for quiescing to work.</p>
232236 */
233237 @ Override
234238 public VMSnapshot takeVMSnapshot (VMSnapshot vmSnapshot ) {
@@ -264,12 +268,19 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
264268 current = vmSnapshotHelper .getSnapshotWithParents (currentSnapshot );
265269 }
266270
267- // For ONTAP managed NFS, always quiesce the VM for filesystem consistency
268- boolean quiescevm = true ;
271+ // Respect the user's quiesce option from the VM snapshot request
272+ boolean quiescevm = true ; // default to true for safety
269273 VMSnapshotOptions options = vmSnapshotVO .getOptions ();
270- if (options != null && !options .needQuiesceVM ()) {
271- logger .info ("Quiesce option was set to false, but overriding to true for ONTAP managed storage " +
272- "to ensure filesystem consistency across all volumes" );
274+ if (options != null ) {
275+ quiescevm = options .needQuiesceVM ();
276+ }
277+
278+ if (quiescevm ) {
279+ logger .info ("Quiesce option is enabled for ONTAP VM Snapshot of VM [{}]. " +
280+ "VM file systems will be frozen/thawed for application-consistent snapshots." , userVm .getInstanceName ());
281+ } else {
282+ logger .info ("Quiesce option is disabled for ONTAP VM Snapshot of VM [{}]. " +
283+ "Snapshots will be crash-consistent only." , userVm .getInstanceName ());
273284 }
274285
275286 VMSnapshotTO target = new VMSnapshotTO (vmSnapshot .getId (), vmSnapshot .getName (),
@@ -284,7 +295,7 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
284295 CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand (
285296 userVm .getInstanceName (), userVm .getUuid (), target , volumeTOs , guestOS .getDisplayName ());
286297
287- logger .info ("Creating ONTAP VM Snapshot for VM [{}] with quiesce=true " , userVm .getInstanceName ());
298+ logger .info ("Creating ONTAP VM Snapshot for VM [{}] with quiesce={} " , userVm .getInstanceName (), quiescevm );
288299
289300 // Prepare volume info list
290301 List <VolumeInfo > volumeInfos = new ArrayList <>();
@@ -295,22 +306,26 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
295306 prev_chain_size += volumeVO .getVmSnapshotChainSize () == null ? 0 : volumeVO .getVmSnapshotChainSize ();
296307 }
297308
298- // ── Step 1: Freeze the VM ──
299- FreezeThawVMCommand freezeCommand = new FreezeThawVMCommand (userVm .getInstanceName ());
300- freezeCommand .setOption (FreezeThawVMCommand .FREEZE );
301- freezeAnswer = (FreezeThawVMAnswer ) agentMgr .send (hostId , freezeCommand );
302- startFreeze = System .nanoTime ();
309+ // ── Step 1: Freeze the VM (only if quiescing is requested) ──
310+ if (quiescevm ) {
311+ FreezeThawVMCommand freezeCommand = new FreezeThawVMCommand (userVm .getInstanceName ());
312+ freezeCommand .setOption (FreezeThawVMCommand .FREEZE );
313+ freezeAnswer = (FreezeThawVMAnswer ) agentMgr .send (hostId , freezeCommand );
314+ startFreeze = System .nanoTime ();
303315
304- thawCmd = new FreezeThawVMCommand (userVm .getInstanceName ());
305- thawCmd .setOption (FreezeThawVMCommand .THAW );
316+ thawCmd = new FreezeThawVMCommand (userVm .getInstanceName ());
317+ thawCmd .setOption (FreezeThawVMCommand .THAW );
306318
307- if (freezeAnswer == null || !freezeAnswer .getResult ()) {
308- String detail = (freezeAnswer != null ) ? freezeAnswer .getDetails () : "no response from agent" ;
309- throw new CloudRuntimeException ("Could not freeze VM [" + userVm .getInstanceName () +
310- "] for ONTAP snapshot. Ensure qemu-guest-agent is installed and running. Details: " + detail );
311- }
319+ if (freezeAnswer == null || !freezeAnswer .getResult ()) {
320+ String detail = (freezeAnswer != null ) ? freezeAnswer .getDetails () : "no response from agent" ;
321+ throw new CloudRuntimeException ("Could not freeze VM [" + userVm .getInstanceName () +
322+ "] for ONTAP snapshot. Ensure qemu-guest-agent is installed and running. Details: " + detail );
323+ }
312324
313- logger .info ("VM [{}] frozen successfully via QEMU guest agent" , userVm .getInstanceName ());
325+ logger .info ("VM [{}] frozen successfully via QEMU guest agent" , userVm .getInstanceName ());
326+ } else {
327+ logger .info ("Skipping VM freeze for VM [{}] as quiesce is not requested" , userVm .getInstanceName ());
328+ }
314329
315330 // ── Step 2: Create per-volume snapshots (ONTAP file clones) ──
316331 try {
@@ -328,19 +343,21 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
328343 TimeUnit .MILLISECONDS .convert (System .nanoTime () - startSnapshot , TimeUnit .NANOSECONDS ));
329344 }
330345 } finally {
331- // ── Step 3: Thaw the VM (always, even on error) ──
332- try {
333- thawAnswer = (FreezeThawVMAnswer ) agentMgr .send (hostId , thawCmd );
334- if (thawAnswer != null && thawAnswer .getResult ()) {
335- logger .info ("VM [{}] thawed successfully. Total freeze duration: {} ms" ,
336- userVm .getInstanceName (),
337- TimeUnit .MILLISECONDS .convert (System .nanoTime () - startFreeze , TimeUnit .NANOSECONDS ));
338- } else {
339- logger .warn ("Failed to thaw VM [{}]: {}" , userVm .getInstanceName (),
340- (thawAnswer != null ) ? thawAnswer .getDetails () : "no response" );
346+ // ── Step 3: Thaw the VM (only if it was frozen, always even on error) ──
347+ if (quiescevm && freezeAnswer != null && freezeAnswer .getResult ()) {
348+ try {
349+ thawAnswer = (FreezeThawVMAnswer ) agentMgr .send (hostId , thawCmd );
350+ if (thawAnswer != null && thawAnswer .getResult ()) {
351+ logger .info ("VM [{}] thawed successfully. Total freeze duration: {} ms" ,
352+ userVm .getInstanceName (),
353+ TimeUnit .MILLISECONDS .convert (System .nanoTime () - startFreeze , TimeUnit .NANOSECONDS ));
354+ } else {
355+ logger .warn ("Failed to thaw VM [{}]: {}" , userVm .getInstanceName (),
356+ (thawAnswer != null ) ? thawAnswer .getDetails () : "no response" );
357+ }
358+ } catch (Exception thawEx ) {
359+ logger .error ("Exception while thawing VM [{}]: {}" , userVm .getInstanceName (), thawEx .getMessage (), thawEx );
341360 }
342- } catch (Exception thawEx ) {
343- logger .error ("Exception while thawing VM [{}]: {}" , userVm .getInstanceName (), thawEx .getMessage (), thawEx );
344361 }
345362 }
346363
0 commit comments