@@ -96,13 +96,15 @@ public Answer execute(ClvmLockTransferCommand cmd, LibvirtComputingResource serv
9696 }
9797
9898 /**
99- * Query which host currently holds the CLVM lock for a volume .
100- * Executes: lvs -o lv_attr,lv_host --noheadings <lvPath>
99+ * Query whether this host currently has the CLVM LV activated locally .
100+ * Executes: lvs -o lv_attr,lv_host,lv_active --noheadings <lvPath>
101101 *
102- * This queries the actual CLVM lock state (source of truth).
103- * The lv_host attribute shows which host currently has the volume activated.
104- *
105- * @return ClvmLockTransferAnswer with lock holder hostname
102+ * lv_attr[4]=='a' (isActive) is LOCAL and is the authoritative signal — true only on
103+ * the host where the LV is currently activated. The management server fans out this
104+ * query to all cluster hosts; the one returning isActive=true is the lock holder.
105+ * lv_attr[5]=='o' (isOpen) means a VM has the device open on this host (doing I/O).
106+ * lv_host is retained for diagnostic logging only — do NOT use it to identify the
107+ * lock holder.
106108 */
107109 private Answer handleQueryLockState (ClvmLockTransferCommand cmd , String lvPath , String volumeUuid ) {
108110 try {
@@ -121,14 +123,11 @@ private Answer handleQueryLockState(ClvmLockTransferCommand cmd, String lvPath,
121123 String .format ("lvs command failed: %s" , result ));
122124 }
123125
124- // We need to find the line that contains the actual lv_attr (starts with '-' or other attr chars)
125126 String [] lines = parser .getLines ().split ("\n " );
126127 String dataLine = null ;
127128
128129 for (String line : lines ) {
129130 String trimmed = line .trim ();
130- // Skip empty lines and warning messages
131- // lv_attr always starts with '-', 'w', 'r', etc. and is at least 10 characters
132131 if (!trimmed .isEmpty () &&
133132 trimmed .length () >= 10 &&
134133 (trimmed .charAt (0 ) == '-' || trimmed .charAt (0 ) == 'w' ||
@@ -157,23 +156,27 @@ private Answer handleQueryLockState(ClvmLockTransferCommand cmd, String lvPath,
157156 }
158157
159158 String lvAttr = parts [0 ];
159+ // lv_host: for diagnostics only, unreliable for lock-holder identification
160160 String hostname = parts .length > 1 ? parts [1 ] : null ;
161161
162+ // lv_attr[4]=='a' → LV is active on THIS host (local activation state)
162163 boolean isActive = lvAttr .length () > 4 && lvAttr .charAt (4 ) == 'a' ;
163- boolean isExclusive = lvAttr .length () > 5 && lvAttr .charAt (5 ) == 'e' ;
164+ // lv_attr[5]=='o' → a process has the device file open on this host (VM doing I/O)
165+ boolean isOpen = lvAttr .length () > 5 && lvAttr .charAt (5 ) == 'o' ;
164166
165- logger .info ("Queried lock state for volume {}: attr={}, hostname={}, active={}, exclusive ={}" ,
166- volumeUuid , lvAttr , hostname , isActive , isExclusive );
167+ logger .info ("Queried lock state for volume {}: attr={}, hostname={}, active={}, open ={}" ,
168+ volumeUuid , lvAttr , hostname , isActive , isOpen );
167169
168170 return new ClvmLockTransferAnswer (cmd , true ,
169- String .format ("Lock state: active=%s, exclusive =%s, host=%s" ,
170- isActive , isExclusive , hostname != null ? hostname : "none" ),
171- hostname , isActive , isExclusive , lvAttr );
171+ String .format ("Lock state: active=%s, open =%s, host=%s" ,
172+ isActive , isOpen , hostname != null ? hostname : "none" ),
173+ hostname , isActive , isOpen , lvAttr );
172174
173175 } catch (Exception e ) {
174176 logger .error ("Exception during lock state query for volume {}: {}" ,
175177 volumeUuid , e .getMessage (), e );
176178 return new ClvmLockTransferAnswer (cmd , false , "Exception: " + e .getMessage ());
177179 }
178180 }
181+
179182}
0 commit comments