3232import java .util .concurrent .ConcurrentHashMap ;
3333import java .util .stream .Collectors ;
3434
35+ import com .cloud .utils .Pair ;
3536import org .apache .cloudstack .api .ApiConstants ;
3637import org .apache .cloudstack .utils .cryptsetup .KeyFile ;
3738import org .apache .cloudstack .utils .qemu .QemuImageOptions ;
8081import com .cloud .utils .exception .CloudRuntimeException ;
8182import com .cloud .utils .script .Script ;
8283
84+ import com .google .gson .JsonElement ;
85+ import com .google .gson .JsonObject ;
86+ import com .google .gson .JsonArray ;
87+ import com .google .gson .JsonParser ;
88+ import java .io .BufferedReader ;
89+ import java .io .InputStreamReader ;
90+
8391public class LibvirtStorageAdaptor implements StorageAdaptor {
8492 protected Logger logger = LogManager .getLogger (getClass ());
8593 private StorageLayer _storageLayer ;
@@ -703,10 +711,17 @@ public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) {
703711 logger .info ("Asking libvirt to refresh storage pool " + uuid );
704712 pool .refresh ();
705713 }
706- pool .setCapacity (storage .getInfo ().capacity );
707- pool .setUsed (storage .getInfo ().allocation );
708- updateLocalPoolIops (pool );
709- pool .setAvailable (storage .getInfo ().available );
714+ if (pool .getType () == StoragePoolType .RBD ) {
715+ // queryCephStats 메서드 호출 (직접 Ceph에서 정확한 수치 조회)
716+ Pair <Long , Long > cephStats = queryCephStats ("rbd" );
717+ pool .setCapacity (cephStats .first ());
718+ pool .setUsed (cephStats .second ());
719+ pool .setAvailable (cephStats .first () - cephStats .second ());
720+ } else {
721+ pool .setCapacity (storage .getInfo ().capacity );
722+ pool .setUsed (storage .getInfo ().allocation );
723+ pool .setAvailable (storage .getInfo ().available );
724+ }
710725
711726 logger .debug ("Successfully refreshed pool " + uuid +
712727 " Capacity: " + toHumanReadableSize (storage .getInfo ().capacity ) +
@@ -720,6 +735,59 @@ public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) {
720735 }
721736 }
722737
738+ /**
739+ * Ceph 풀의 저장량과 총 논리 용량을 계산하여 반환합니다.
740+ * Returns the logical used and total capacity for a given Ceph pool.
741+ * - 사용량은 stored 값을 기반으로 하며 (복제 미포함)
742+ * - 총 용량은 stored + (max_avail / replicationFactor) 방식으로 계산합니다.
743+ *
744+ * @param poolName Ceph 풀 이름 / Name of the Ceph pool (e.g., "rbd")
745+ * @return (총 용량, 사용량) / (total capacity, used capacity) in bytes
746+ */
747+ public static Pair <Long , Long > queryCephStats (String poolName ) {
748+ try {
749+ Process process = new ProcessBuilder ("ceph" , "df" , "--format=json" ).start ();
750+ BufferedReader reader = new BufferedReader (new InputStreamReader (process .getInputStream ()));
751+ StringBuilder output = new StringBuilder ();
752+
753+ String line ;
754+ while ((line = reader .readLine ()) != null ) {
755+ output .append (line );
756+ }
757+ process .waitFor ();
758+
759+ JsonParser parser = new JsonParser ();
760+ JsonObject root = parser .parse (output .toString ()).getAsJsonObject ();
761+ JsonArray pools = root .getAsJsonArray ("pools" );
762+
763+ long stored = 0 ;
764+ long maxAvail = 0 ;
765+
766+ for (JsonElement el : pools ) {
767+ JsonObject pool = el .getAsJsonObject ();
768+ String name = pool .get ("name" ).getAsString ();
769+
770+ if (!name .equals (poolName )) {
771+ continue ;
772+ }
773+
774+ JsonObject stats = pool .getAsJsonObject ("stats" );
775+ stored = stats .get ("stored" ).getAsLong ();
776+ maxAvail = stats .get ("max_avail" ).getAsLong ();
777+ break ;
778+ }
779+
780+ long logicalMaxAvail = maxAvail ;
781+ long total = stored + logicalMaxAvail ;
782+ long used = stored ;
783+
784+ return new Pair <>(total , used );
785+
786+ } catch (Exception e ) {
787+ throw new CloudRuntimeException ("Failed to execute 'ceph df --format=json': " + e .getMessage (), e );
788+ }
789+ }
790+
723791 @ Override
724792 public KVMPhysicalDisk getPhysicalDisk (String volumeUuid , KVMStoragePool pool ) {
725793 LibvirtStoragePool libvirtPool = (LibvirtStoragePool )pool ;
0 commit comments