Repository: kubevirt-ui/kubevirt-plugin
Pull request #2679: CNV-57330: Use DataVolume size for InstanceType VM creation
Author: login: pcbailey
Labels: name: lgtm
description: Passed code review, ready for merge
name: approved
description: This issue is something we want to fix
name: jira/valid-reference
Created: 2025-05-20T20:08:02Z | Merged: 2025-05-28T15:28:17Z

## 📝 Description

This PR updates the Instance Types VM creation flow logic to use the size of the DataVolume over that of the PVC if it exists. The size of PVCs created using a filesystem-based storage class have already been increased to include overhead, so using the already increased size would result in double the overhead.

Jira: https://issues.redhat.com/browse/CNV-57330

## 🎥 Screenshots

### Before

![use-dv-size-for-instancetypes-vm-creation--BEFORE--2025-05-20_11-58](https://github.com/user-attachments/assets/5b4e0326-5a06-43c7-8f5b-52bd5485439c)

### After

![use-dv-size-for-instancetypes-vm-creation--AFTER--2025-05-20_11-45](https://github.com/user-attachments/assets/b6a2ba98-b6a4-4b2b-815f-4bafc09f8020)


Diff:
diff --git a/src/utils/components/AddBootableVolumeModal/AddBootableVolumeModal.tsx b/src/utils/components/AddBootableVolumeModal/AddBootableVolumeModal.tsx
index e9d1388ea8..3b28335c3f 100644
--- a/src/utils/components/AddBootableVolumeModal/AddBootableVolumeModal.tsx
+++ b/src/utils/components/AddBootableVolumeModal/AddBootableVolumeModal.tsx
@@ -1,19 +1,16 @@
 import React, { FC, useCallback, useState } from 'react';
 
+import { InstanceTypeVMStoreActions } from '@catalog/CreateFromInstanceTypes/state/utils/types';
 import { DEFAULT_PREFERENCE_LABEL } from '@catalog/CreateFromInstanceTypes/utils/constants';
-import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import HelpTextIcon from '@kubevirt-utils/components/HelpTextIcon/HelpTextIcon';
 import TabModal from '@kubevirt-utils/components/TabModal/TabModal';
 import { useCDIUpload } from '@kubevirt-utils/hooks/useCDIUpload/useCDIUpload';
 import { UPLOAD_STATUS } from '@kubevirt-utils/hooks/useCDIUpload/utils';
 import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
-import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
 import { getValidNamespace, kubevirtConsole } from '@kubevirt-utils/utils/utils';
 import { useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk';
 import { Form, PopoverPosition, Title } from '@patternfly/react-core';
 
-import { VolumeSnapshotKind } from '../SelectSnapshot/types';
-
 import SchedulingSettings from './components/SchedulingSettings';
 import SourceTypeSelection from './components/SourceTypeSelection/SourceTypeSelection';
 import VolumeDestination from './components/VolumeDestination/VolumeDestination';
@@ -33,11 +30,7 @@ import './AddBootableVolumeModal.scss';
 type AddBootableVolumeModalProps = {
   isOpen: boolean;
   onClose: () => void;
-  onCreateVolume?: (
-    source: BootableVolume,
-    pvcSource?: IoK8sApiCoreV1PersistentVolumeClaim,
-    volumeSnapshotSource?: VolumeSnapshotKind,
-  ) => void;
+  onCreateVolume?: InstanceTypeVMStoreActions['onSelectCreatedVolume'];
 };
 
 const AddBootableVolumeModal: FC<AddBootableVolumeModalProps> = ({
diff --git a/src/utils/resources/bootableresources/helpers.ts b/src/utils/resources/bootableresources/helpers.ts
index b38702d2d1..9d140fa500 100644
--- a/src/utils/resources/bootableresources/helpers.ts
+++ b/src/utils/resources/bootableresources/helpers.ts
@@ -12,15 +12,21 @@ import {
   V1beta1DataVolume,
 } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
+import {
+  getDataSourcePVCName,
+  getDataSourcePVCNamespace,
+} from '@kubevirt-utils/resources/bootableresources/selectors';
 import { isEmpty, kubevirtConsole } from '@kubevirt-utils/utils/utils';
 import { k8sDelete } from '@openshift-console/dynamic-plugin-sdk';
 
-import { getLabel, getName, getNamespace } from '../shared';
+import { getLabel, getName, getNamespace, NamespacedResourceMap } from '../shared';
 
 import { deprecatedOSNames, KUBEVIRT_ISO_LABEL } from './constants';
 import { BootableVolume } from './types';
 
-export const isBootableVolumePVCKind = (bootableVolume: BootableVolume): boolean =>
+export const isBootableVolumePVCKind = (
+  bootableVolume: BootableVolume,
+): bootableVolume is IoK8sApiCoreV1PersistentVolumeClaim =>
   bootableVolume?.kind === PersistentVolumeClaimModel.kind;
 
 export const getBootableVolumeGroupVersionKind = (bootableVolume: BootableVolume) =>
@@ -30,19 +36,22 @@ export const getBootableVolumeGroupVersionKind = (bootableVolume: BootableVolume
 
 export const getBootableVolumePVCSource = (
   bootableVolume: BootableVolume,
-  pvcSources: {
-    [resourceKeyName: string]: IoK8sApiCoreV1PersistentVolumeClaim;
-  },
+  pvcSources: NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>,
 ): IoK8sApiCoreV1PersistentVolumeClaim | null => {
   if (isEmpty(bootableVolume)) return null;
 
   return isBootableVolumePVCKind(bootableVolume)
     ? bootableVolume
-    : pvcSources?.[(bootableVolume as V1beta1DataSource)?.spec?.source?.pvc?.namespace]?.[
-        (bootableVolume as V1beta1DataSource)?.spec?.source?.pvc?.name
+    : pvcSources?.[getDataSourcePVCNamespace(bootableVolume as V1beta1DataSource)]?.[
+        getDataSourcePVCName(bootableVolume as V1beta1DataSource)
       ];
 };
 
+export const getDataVolumeForPVC = (
+  pvc: IoK8sApiCoreV1PersistentVolumeClaim,
+  dvSources: NamespacedResourceMap<V1beta1DataVolume>,
+) => (pvc ? dvSources?.[getNamespace(pvc)]?.[getName(pvc)] : null);
+
 export const getInstanceTypePrefix = (instanceTypeNamePrefix: string): string => {
   if (instanceTypeNamePrefix?.includes('.')) {
     return instanceTypeNamePrefix?.split('.')?.[0];
diff --git a/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts b/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts
index e0bd964542..a9af74b260 100644
--- a/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts
+++ b/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts
@@ -9,9 +9,11 @@ import {
   VolumeSnapshotModel,
 } from '@kubevirt-ui/kubevirt-api/console';
 import DataImportCronModel from '@kubevirt-ui/kubevirt-api/console/models/DataImportCronModel';
+import DataVolumeModel from '@kubevirt-ui/kubevirt-api/console/models/DataVolumeModel';
 import {
   V1beta1DataImportCron,
   V1beta1DataSource,
+  V1beta1DataVolume,
 } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/types';
@@ -57,6 +59,12 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => {
     namespace: projectsNamespace,
   });
 
+  const [dvs, loadedDVs, loadErrorDVs] = useK8sWatchResource<V1beta1DataVolume[]>({
+    groupVersionKind: modelToGroupVersionKind(DataVolumeModel),
+    isList: true,
+    namespace: projectsNamespace,
+  });
+
   // getting volumesnapshot as this can also be a source of DS
   const [volumeSnapshots] = useK8sWatchResource<VolumeSnapshotKind[]>({
     groupVersionKind: modelToGroupVersionKind(VolumeSnapshotModel),
@@ -65,13 +73,13 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => {
   });
 
   const error = useMemo(
-    () => dataSourcesError || loadErrorPVCs || dataImportCronsError,
-    [dataSourcesError, loadErrorPVCs, dataImportCronsError],
+    () => dataSourcesError || loadErrorDVs || loadErrorPVCs || dataImportCronsError,
+    [dataSourcesError, loadErrorDVs, loadErrorPVCs, dataImportCronsError],
   );
 
   const loaded = useMemo(
-    () => (error ? true : loadedDataSources && loadedPVCs && loadedDataImportCrons),
-    [error, loadedDataSources, loadedPVCs, loadedDataImportCrons],
+    () => (error ? true : loadedDataSources && loadedDVs && loadedPVCs && loadedDataImportCrons),
+    [error, loadedDataSources, loadedDVs, loadedPVCs, loadedDataImportCrons],
   );
 
   const readyOrCloningDataSources = useMemo(
@@ -79,6 +87,7 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => {
     [dataSources, dataImportCrons],
   );
   const pvcSources = useMemo(() => convertResourceArrayToMap(pvcs, true), [pvcs]);
+  const dvSources = useMemo(() => convertResourceArrayToMap(dvs, true), [dvs]);
 
   const bootableVolumes: BootableVolume[] = useMemo(() => {
     const dataSourceVolumes =
@@ -105,6 +114,7 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => {
   return {
     bootableVolumes,
     dataImportCrons,
+    dvSources,
     error,
     loaded,
     pvcSources,
diff --git a/src/utils/resources/bootableresources/selectors.ts b/src/utils/resources/bootableresources/selectors.ts
index 3bc9f2b1f6..fc771a20f1 100644
--- a/src/utils/resources/bootableresources/selectors.ts
+++ b/src/utils/resources/bootableresources/selectors.ts
@@ -9,6 +9,12 @@ import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/ty
 export const getDataSourcePVCSource = (dataSource: V1beta1DataSource): V1beta1DataVolumeSourcePVC =>
   dataSource?.spec?.source?.pvc;
 
+export const getDataSourcePVCName = (dataSource: V1beta1DataSource) =>
+  getDataSourcePVCSource(dataSource)?.name;
+
+export const getDataSourcePVCNamespace = (dataSource: V1beta1DataSource) =>
+  getDataSourcePVCSource(dataSource)?.namespace;
+
 export const getVolumeSnapshotSize = (volumeSnapshot: VolumeSnapshotKind) =>
   volumeSnapshot?.status?.restoreSize;
 
@@ -18,6 +24,9 @@ export const getVolumeSnapshotStorageClass = (volumeSnapshot: VolumeSnapshotKind
 export const getPVCStorageCapacity = (pvc: IoK8sApiCoreV1PersistentVolumeClaim) =>
   pvc?.status?.capacity?.storage;
 
+export const getPVCSize = (pvc: IoK8sApiCoreV1PersistentVolumeClaim) =>
+  pvc?.spec?.resources?.requests?.storage;
+
 export const getPVCStorageClassName = (pvc: IoK8sApiCoreV1PersistentVolumeClaim) =>
   pvc?.spec?.storageClassName;
 
diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/BootableVolumeList.tsx b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/BootableVolumeList.tsx
index 78d7157a4e..856814b8ee 100644
--- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/BootableVolumeList.tsx
+++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/BootableVolumeList.tsx
@@ -59,8 +59,9 @@ const BootableVolumeList: FC<BootableVolumeListProps> = ({
     volumeListNamespace,
   } = useInstanceTypeVMStore();
 
-  const { pvcSource, selectedBootableVolume, volumeSnapshotSource } = instanceTypeVMState;
-  const { bootableVolumes, loaded, pvcSources, volumeSnapshotSources } = bootableVolumesData;
+  const { dvSource, pvcSource, selectedBootableVolume, volumeSnapshotSource } = instanceTypeVMState;
+  const { bootableVolumes, dvSources, loaded, pvcSources, volumeSnapshotSources } =
+    bootableVolumesData;
 
   const preferencesMap = useMemo(
     () => convertResourceArrayToMap(preferencesData),
@@ -92,6 +93,7 @@ const BootableVolumeList: FC<BootableVolumeListProps> = ({
     volumeSnapshotSources,
     pagination,
     volumeListNamespace === ALL_PROJECTS,
+    dvSources,
   );
 
   useEffect(() => {
@@ -111,7 +113,7 @@ const BootableVolumeList: FC<BootableVolumeListProps> = ({
 
     setPagination(getPaginationFromVolumeIndex(selectedVolumeIndex));
 
-    onSelectCreatedVolume(modalSelectedVolume, pvcSource, volumeSnapshotSource);
+    onSelectCreatedVolume(modalSelectedVolume, pvcSource, volumeSnapshotSource, dvSource);
   };
 
   return (
diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx
index 34b17225ea..37153fa7ba 100644
--- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx
+++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx
@@ -2,10 +2,12 @@ import React, { FC, MouseEvent } from 'react';
 
 import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
 import { InstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/utils/types';
+import { getDiskSize } from '@catalog/CreateFromInstanceTypes/utils/utils';
 import { getTemplateOSIcon, getVolumeNameOSIcon } from '@catalog/templatescatalog/utils/os-icons';
 import {
   V1beta1DataImportCron,
   V1beta1DataSource,
+  V1beta1DataVolume,
 } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import { V1beta1VirtualMachineClusterPreference } from '@kubevirt-ui/kubevirt-api/kubevirt';
@@ -16,10 +18,7 @@ import { BOOTABLE_VOLUME_SELECTED } from '@kubevirt-utils/extensions/telemetry/u
 import { ALL_PROJECTS } from '@kubevirt-utils/hooks/constants';
 import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
 import { isDeprecated } from '@kubevirt-utils/resources/bootableresources/helpers';
-import {
-  getVolumeSnapshotSize,
-  getVolumeSnapshotStorageClass,
-} from '@kubevirt-utils/resources/bootableresources/selectors';
+import { getVolumeSnapshotStorageClass } from '@kubevirt-utils/resources/bootableresources/selectors';
 import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
 import {
   getName,
@@ -46,6 +45,7 @@ type BootableVolumeRowProps = {
   rowData: {
     bootableVolumeSelectedState: [BootableVolume, InstanceTypeVMStore['onSelectCreatedVolume']];
     dataImportCron: V1beta1DataImportCron;
+    dvSource: V1beta1DataVolume;
     favorites: [isFavorite: boolean, updaterFavorites: (val: boolean) => void];
     preference: V1beta1VirtualMachineClusterPreference;
     pvcSource: IoK8sApiCoreV1PersistentVolumeClaim;
@@ -59,6 +59,7 @@ const BootableVolumeRow: FC<BootableVolumeRowProps> = ({
   rowData: {
     bootableVolumeSelectedState: [selectedBootableVolume, setSelectedBootableVolume],
     dataImportCron,
+    dvSource,
     favorites,
     preference,
     pvcSource,
@@ -68,15 +69,13 @@ const BootableVolumeRow: FC<BootableVolumeRowProps> = ({
   const { t } = useKubevirtTranslation();
   const bootVolumeName = getName(bootableVolume);
   const bootVolumeNamespace = getNamespace(bootableVolume);
-  const sizeData = formatBytes(
-    pvcSource?.spec?.resources?.requests?.storage || getVolumeSnapshotSize(volumeSnapshotSource),
-  );
+  const sizeData = formatBytes(getDiskSize(dvSource, pvcSource, volumeSnapshotSource));
   const icon = getVolumeNameOSIcon(bootVolumeName) || getTemplateOSIcon(preference);
 
   const [isFavorite, addOrRemoveFavorite] = favorites;
 
   const handleOnClick = () => {
-    setSelectedBootableVolume(bootableVolume, pvcSource, volumeSnapshotSource);
+    setSelectedBootableVolume(bootableVolume, pvcSource, volumeSnapshotSource, dvSource);
     logITFlowEvent(BOOTABLE_VOLUME_SELECTED, null, {
       selectedBootableVolume: getName(bootableVolume),
     });
diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx
index e164f55f7a..c01c05208d 100644
--- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx
+++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx
@@ -11,6 +11,7 @@ import { UserSettingFavorites } from '@kubevirt-utils/hooks/useKubevirtUserSetti
 import {
   getBootableVolumePVCSource,
   getDataImportCronFromDataSource,
+  getDataVolumeForPVC,
 } from '@kubevirt-utils/resources/bootableresources/helpers';
 import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
 import { getLabel, getName } from '@kubevirt-utils/resources/shared';
@@ -43,7 +44,7 @@ const BootableVolumeTable: FC<BootableVolumeTableProps> = ({
   sortedPaginatedData,
 }) => {
   const [volumeFavorites, updateFavorites] = favorites;
-  const { dataImportCrons, pvcSources, volumeSnapshotSources } = bootableVolumesData;
+  const { dataImportCrons, dvSources, pvcSources, volumeSnapshotSources } = bootableVolumesData;
 
   return (
     <Table className="BootableVolumeList-table" variant={TableVariant.compact}>
@@ -65,32 +66,38 @@ const BootableVolumeTable: FC<BootableVolumeTableProps> = ({
         </Tr>
       </Thead>
       <Tbody>
-        {sortedPaginatedData.map((bs) => (
-          <BootableVolumeRow
-            rowData={{
-              bootableVolumeSelectedState: selectedBootableVolumeState,
-              dataImportCron: getDataImportCronFromDataSource(
-                dataImportCrons,
-                bs as V1beta1DataSource,
-              ),
-              favorites: [
-                volumeFavorites?.includes(bs?.metadata?.name),
-                (addTofavorites: boolean) =>
-                  updateFavorites(
-                    addTofavorites
-                      ? [...volumeFavorites, bs?.metadata?.name]
-                      : volumeFavorites.filter((fav: string) => fav !== bs?.metadata?.name),
-                  ),
-              ],
-              preference: preferencesMap[getLabel(bs, DEFAULT_PREFERENCE_LABEL)],
-              pvcSource: getBootableVolumePVCSource(bs, pvcSources),
-              volumeSnapshotSource: volumeSnapshotSources?.[bs?.metadata?.name],
-            }}
-            activeColumnIDs={activeColumns?.map((col) => col?.id)}
-            bootableVolume={bs}
-            key={getName(bs)}
-          />
-        ))}
+        {sortedPaginatedData.map((bootSource) => {
+          const bootSourceName = getName(bootSource);
+          const pvcSource = getBootableVolumePVCSource(bootSource, pvcSources);
+
+          return (
+            <BootableVolumeRow
+              rowData={{
+                bootableVolumeSelectedState: selectedBootableVolumeState,
+                dataImportCron: getDataImportCronFromDataSource(
+                  dataImportCrons,
+                  bootSource as V1beta1DataSource,
+                ),
+                dvSource: getDataVolumeForPVC(pvcSource, dvSources),
+                favorites: [
+                  volumeFavorites?.includes(bootSourceName),
+                  (addTofavorites: boolean) =>
+                    updateFavorites(
+                      addTofavorites
+                        ? [...volumeFavorites, bootSourceName]
+                        : volumeFavorites.filter((fav: string) => fav !== bootSourceName),
+                    ),
+                ],
+                preference: preferencesMap[getLabel(bootSource, DEFAULT_PREFERENCE_LABEL)],
+                pvcSource,
+                volumeSnapshotSource: volumeSnapshotSources?.[bootSourceName],
+              }}
+              activeColumnIDs={activeColumns?.map((col) => col?.id)}
+              bootableVolume={bootSource}
+              key={bootSourceName}
+            />
+          );
+        })}
       </Tbody>
     </Table>
   );
diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/hooks/useBootVolumeSortColumns.ts b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/hooks/useBootVolumeSortColumns.ts
index 7920cd9fbc..15aa43751a 100644
--- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/hooks/useBootVolumeSortColumns.ts
+++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/hooks/useBootVolumeSortColumns.ts
@@ -1,34 +1,38 @@
 import { useState } from 'react';
 
 import { DEFAULT_PREFERENCE_LABEL } from '@catalog/CreateFromInstanceTypes/utils/constants';
+import { getDiskSize } from '@catalog/CreateFromInstanceTypes/utils/utils';
+import { V1beta1DataVolume } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import { V1beta1VirtualMachineClusterPreference } from '@kubevirt-ui/kubevirt-api/kubevirt';
 import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/types';
 import { PaginationState } from '@kubevirt-utils/hooks/usePagination/utils/types';
-import { getBootableVolumePVCSource } from '@kubevirt-utils/resources/bootableresources/helpers';
 import {
-  getVolumeSnapshotSize,
-  getVolumeSnapshotStorageClass,
-} from '@kubevirt-utils/resources/bootableresources/selectors';
+  getBootableVolumePVCSource,
+  getDataVolumeForPVC,
+} from '@kubevirt-utils/resources/bootableresources/helpers';
+import { getVolumeSnapshotStorageClass } from '@kubevirt-utils/resources/bootableresources/selectors';
 import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
-import { getName, getNamespace } from '@kubevirt-utils/resources/shared';
+import {
+  getName,
+  getNamespace,
+  NamespacedResourceMap,
+  ResourceMap,
+} from '@kubevirt-utils/resources/shared';
 import { DESCRIPTION_ANNOTATION } from '@kubevirt-utils/resources/vm';
 import { ThSortType } from '@patternfly/react-table/dist/esm/components/Table/base/types';
 
 type UseBootVolumeSortColumns = (
   unsortedData: BootableVolume[],
   volumeFavorites: string[],
-  preferences: {
-    [resourceKeyName: string]: V1beta1VirtualMachineClusterPreference;
-  },
-  pvcSources: {
-    [resourceKeyName: string]: IoK8sApiCoreV1PersistentVolumeClaim;
-  },
+  preferences: ResourceMap<V1beta1VirtualMachineClusterPreference>,
+  pvcSources: NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>,
   volumeSnapshotSources: {
     [datSourceName: string]: VolumeSnapshotKind;
   },
   pagination: PaginationState,
   includeNamespaceColumn: boolean,
+  dvSources: NamespacedResourceMap<V1beta1DataVolume>,
 ) => {
   getSortType: (columnIndex: number) => ThSortType;
   sortedData: BootableVolume[];
@@ -43,12 +47,14 @@ const useBootVolumeSortColumns: UseBootVolumeSortColumns = (
   volumeSnapshotSources,
   pagination,
   includeNamespaceColumn,
+  dvSources,
 ) => {
   const [activeSortIndex, setActiveSortIndex] = useState<null | number>(0);
   const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>('asc');
 
   const getSortableRowValues = (bootableVolume: BootableVolume): string[] => {
     const pvcSource = getBootableVolumePVCSource(bootableVolume, pvcSources);
+    const dvSource = getDataVolumeForPVC(pvcSource, dvSources);
     const volumeSnapshotSource = volumeSnapshotSources?.[bootableVolume?.metadata?.name];
 
     return [
@@ -56,7 +62,7 @@ const useBootVolumeSortColumns: UseBootVolumeSortColumns = (
       ...(includeNamespaceColumn ? [getNamespace(bootableVolume)] : []),
       getName(preferences[bootableVolume?.metadata?.labels?.[DEFAULT_PREFERENCE_LABEL]]),
       pvcSource?.spec?.storageClassName || getVolumeSnapshotStorageClass(volumeSnapshotSource),
-      pvcSource?.spec?.resources?.requests?.storage || getVolumeSnapshotSize(volumeSnapshotSource),
+      getDiskSize(dvSource, pvcSource, volumeSnapshotSource),
       bootableVolume?.metadata?.annotations?.[DESCRIPTION_ANNOTATION],
     ];
   };
diff --git a/src/views/catalog/CreateFromInstanceTypes/components/VMDetailsSection/components/DiskSize.tsx b/src/views/catalog/CreateFromInstanceTypes/components/VMDetailsSection/components/DiskSize.tsx
index d12911cf60..4e609e238b 100644
--- a/src/views/catalog/CreateFromInstanceTypes/components/VMDetailsSection/components/DiskSize.tsx
+++ b/src/views/catalog/CreateFromInstanceTypes/components/VMDetailsSection/components/DiskSize.tsx
@@ -1,18 +1,17 @@
 import React, { FC } from 'react';
 
 import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
+import { getDiskSize } from '@catalog/CreateFromInstanceTypes/utils/utils';
 import CapacityInput from '@kubevirt-utils/components/CapacityInput/CapacityInput';
 import { DEFAULT_DISK_SIZE } from '@kubevirt-utils/components/DiskModal/utils/constants';
-import { getVolumeSnapshotSize } from '@kubevirt-utils/resources/bootableresources/selectors';
 import { convertToBaseValue, humanizeBinaryBytes } from '@kubevirt-utils/utils/humanize.js';
 
 const DiskSize: FC = () => {
   const { instanceTypeVMState, setCustomDiskSize } = useInstanceTypeVMStore();
 
-  const { customDiskSize, pvcSource, volumeSnapshotSource } = instanceTypeVMState;
+  const { customDiskSize, dvSource, pvcSource, volumeSnapshotSource } = instanceTypeVMState;
 
-  const pvcDiskSize =
-    pvcSource?.spec?.resources?.requests?.storage || getVolumeSnapshotSize(volumeSnapshotSource);
+  const pvcDiskSize = getDiskSize(dvSource, pvcSource, volumeSnapshotSource);
   const sizeData = humanizeBinaryBytes(convertToBaseValue(pvcDiskSize || DEFAULT_DISK_SIZE)).string;
 
   return <CapacityInput onChange={setCustomDiskSize} size={customDiskSize || sizeData} />;
diff --git a/src/views/catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore.ts b/src/views/catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore.ts
index f1b34d37a2..4365a55635 100644
--- a/src/views/catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore.ts
+++ b/src/views/catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore.ts
@@ -2,6 +2,7 @@ import produce from 'immer';
 import { create } from 'zustand';
 
 import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel';
+import { V1beta1DataVolume } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
 import { getInstanceTypeFromVolume } from '@kubevirt-utils/components/AddBootableVolumeModal/utils/utils';
@@ -34,9 +35,11 @@ export const useInstanceTypeVMStore = create<InstanceTypeVMStore>()((set, get) =
       selectedVolume: BootableVolume,
       pvcSource: IoK8sApiCoreV1PersistentVolumeClaim,
       volumeSnapshotSource: VolumeSnapshotKind,
+      dvSource: V1beta1DataVolume,
     ) =>
       set(
         produce<InstanceTypeVMStore>(({ instanceTypeVMState }) => {
+          instanceTypeVMState.dvSource = dvSource;
           instanceTypeVMState.selectedBootableVolume = selectedVolume;
           instanceTypeVMState.pvcSource = pvcSource;
           instanceTypeVMState.volumeSnapshotSource = volumeSnapshotSource;
diff --git a/src/views/catalog/CreateFromInstanceTypes/state/utils/state.ts b/src/views/catalog/CreateFromInstanceTypes/state/utils/state.ts
index 3ccd19bac7..0de2252ad3 100644
--- a/src/views/catalog/CreateFromInstanceTypes/state/utils/state.ts
+++ b/src/views/catalog/CreateFromInstanceTypes/state/utils/state.ts
@@ -8,6 +8,7 @@ import { InstanceTypeVMState, InstanceTypeVMStoreState } from './types';
 
 export const instanceTypeVMInitialState: InstanceTypeVMState = {
   customDiskSize: '',
+  dvSource: null,
   folder: '',
   isDynamicSSHInjection: false,
   pvcSource: null,
diff --git a/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts b/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts
index e941e0e539..aa56db93d0 100644
--- a/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts
+++ b/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts
@@ -1,6 +1,9 @@
 import { Dispatch } from 'react';
 
-import { V1beta1DataImportCron } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
+import {
+  V1beta1DataImportCron,
+  V1beta1DataVolume,
+} from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
 import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import {
   V1beta1VirtualMachineClusterInstancetype,
@@ -12,6 +15,7 @@ import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/ty
 import { SSHSecretDetails } from '@kubevirt-utils/components/SSHSecretModal/utils/types';
 import { SysprepData } from '@kubevirt-utils/components/SysprepModal/sysprep-utils';
 import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
+import { NamespacedResourceMap } from '@kubevirt-utils/resources/shared';
 
 export type InstanceTypes = (
   | V1beta1VirtualMachineClusterInstancetype
@@ -29,11 +33,10 @@ export type UseInstanceTypeAndPreferencesValues = {
 export type UseBootableVolumesValues = {
   bootableVolumes: BootableVolume[];
   dataImportCrons: V1beta1DataImportCron[];
+  dvSources: NamespacedResourceMap<V1beta1DataVolume>;
   error: Error;
   loaded: boolean;
-  pvcSources: {
-    [resourceKeyName: string]: IoK8sApiCoreV1PersistentVolumeClaim;
-  };
+  pvcSources: NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>;
   volumeSnapshotSources: { [dataSourceName: string]: VolumeSnapshotKind };
 };
 
@@ -45,6 +48,7 @@ export type SysprepConfigMapData = {
 
 export type InstanceTypeVMState = {
   customDiskSize: string;
+  dvSource: V1beta1DataVolume;
   folder: string;
   isDynamicSSHInjection: boolean;
   pvcSource: IoK8sApiCoreV1PersistentVolumeClaim;
@@ -88,12 +92,13 @@ export type InstanceTypeVMStoreState = {
   volumeListNamespace: string;
 };
 
-type InstanceTypeVMStoreActions = {
+export type InstanceTypeVMStoreActions = {
   applySSHFromSettings: (sshSecretName: string, targetNamespace: string) => void;
   onSelectCreatedVolume: (
     selectedVolume: BootableVolume,
-    pvcSource: IoK8sApiCoreV1PersistentVolumeClaim,
-    volumeSnapshotSource: VolumeSnapshotKind,
+    pvcSource?: IoK8sApiCoreV1PersistentVolumeClaim,
+    volumeSnapshotSource?: VolumeSnapshotKind,
+    dvSource?: V1beta1DataVolume,
   ) => void;
   resetInstanceTypeVMState: () => void;
   setCustomDiskSize: (diskSize: null | string) => void;
diff --git a/src/views/catalog/CreateFromInstanceTypes/utils/utils.ts b/src/views/catalog/CreateFromInstanceTypes/utils/utils.ts
index 73da2b06cc..0f39c790a2 100644
--- a/src/views/catalog/CreateFromInstanceTypes/utils/utils.ts
+++ b/src/views/catalog/CreateFromInstanceTypes/utils/utils.ts
@@ -3,6 +3,8 @@ import produce from 'immer';
 import DataSourceModel from '@kubevirt-ui/kubevirt-api/console/models/DataSourceModel';
 import VirtualMachineInstancetypeModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineInstancetypeModel';
 import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel';
+import { V1beta1DataVolume } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models';
+import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes';
 import { V1Interface, V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
 import {
   addDNFUpdateToRunCMD,
@@ -12,6 +14,7 @@ import {
 } from '@kubevirt-utils/components/CloudinitModal/utils/cloudinit-utils';
 import { DEFAULT_DISK_SIZE } from '@kubevirt-utils/components/DiskModal/utils/constants';
 import { InterfaceTypes } from '@kubevirt-utils/components/DiskModal/utils/types';
+import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/types';
 import { addSecretToVM } from '@kubevirt-utils/components/SSHSecretModal/utils/utils';
 import { sysprepDisk, sysprepVolume } from '@kubevirt-utils/components/SysprepModal/sysprep-utils';
 import { ROOTDISK } from '@kubevirt-utils/constants/constants';
@@ -21,6 +24,11 @@ import {
   isBootableVolumeISO,
   isBootableVolumePVCKind,
 } from '@kubevirt-utils/resources/bootableresources/helpers';
+import {
+  getDataVolumeSize,
+  getPVCSize,
+  getVolumeSnapshotSize,
+} from '@kubevirt-utils/resources/bootableresources/selectors';
 import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types';
 import { getLabel, getName, getNamespace } from '@kubevirt-utils/resources/shared';
 import { OS_NAME_TYPES, OS_NAME_TYPES_NOT_SUPPORTED } from '@kubevirt-utils/resources/template';
@@ -109,6 +117,7 @@ export const generateVM: GenerateVMCallback = ({
   targetNamespace,
 }) => {
   const {
+    dvSource,
     folder,
     pvcSource,
     selectedBootableVolume,
@@ -158,17 +167,18 @@ export const generateVM: GenerateVMCallback = ({
               namespace: getNamespace(selectedBootableVolume),
             },
             storage: {
-              resources: pvcSource
-                ? {
-                    requests: {
-                      storage: pvcSource?.spec?.resources?.requests?.storage,
-                    },
-                  }
-                : {
-                    requests: {
-                      storage: DEFAULT_DISK_SIZE,
+              resources:
+                dvSource || pvcSource
+                  ? {
+                      requests: {
+                        storage: getDataVolumeSize(dvSource) || getPVCSize(pvcSource),
+                      },
+                    }
+                  : {
+                      requests: {
+                        storage: DEFAULT_DISK_SIZE,
+                      },
                     },
-                  },
               storageClassName,
             },
           },
@@ -357,3 +367,9 @@ export const useIsWindowsBootableVolume = (): boolean => {
 
   return defaultPreferenceName?.startsWith(OS_WINDOWS_PREFIX);
 };
+
+export const getDiskSize = (
+  dataVolume: V1beta1DataVolume,
+  pvc: IoK8sApiCoreV1PersistentVolumeClaim,
+  volumeSnapshot: VolumeSnapshotKind,
+) => getDataVolumeSize(dataVolume) || getPVCSize(pvc) || getVolumeSnapshotSize(volumeSnapshot);


Reviews:
user: login: adamviktora
body: Looking great!

A few questions / tips below
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T14:19:22Z
user: login: pcbailey
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-28T12:25:15Z
user: login: pcbailey
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-28T12:26:54Z
user: login: pcbailey
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-28T12:30:38Z
user: login: adamviktora
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-28T13:11:44Z
user: login: pcbailey
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-28T13:22:53Z
user: login: adamviktora
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-28T15:05:33Z

Review comments:
diff_hunk: @@ -69,14 +74,16 @@ const BootableVolumeRow: FC<BootableVolumeRowProps> = ({
   const bootVolumeName = getName(bootableVolume);
   const bootVolumeNamespace = getNamespace(bootableVolume);
   const sizeData = formatBytes(
-    pvcSource?.spec?.resources?.requests?.storage || getVolumeSnapshotSize(volumeSnapshotSource),
+    getDataVolumeSize(dvSource) ||
+      getPVCSize(pvcSource) ||
+      getVolumeSnapshotSize(volumeSnapshotSource),
path: src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx
user: login: adamviktora
body: It looks like your `getDiskSize` helper from utils.ts can be used here
created_at: 2025-05-21T14:01:40Z
author_association: MEMBER
diff_hunk: @@ -29,11 +33,10 @@ export type UseInstanceTypeAndPreferencesValues = {
 export type UseBootableVolumesValues = {
   bootableVolumes: BootableVolume[];
   dataImportCrons: V1beta1DataImportCron[];
+  dvSources: NamespacedResourceMap<V1beta1DataVolume>;
   error: Error;
   loaded: boolean;
-  pvcSources: {
-    [resourceKeyName: string]: IoK8sApiCoreV1PersistentVolumeClaim;
-  };
+  pvcSources: NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>;
path: src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts
user: login: adamviktora
body: Just to be clear, this looks like changing the type from:

`{ [name: string]: IoK8sApiCoreV1PersistentVolumeClaim }`
to:
`{ [namespace: string]: { [name: string]: IoK8sApiCoreV1PersistentVolumeClaim } }`

can it possibly cause issues elsewhere?

But as observing the other code it looks like the type was wrong before and this fixes it.
created_at: 2025-05-21T14:10:55Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,11 @@ export const getBootableVolumePVCSource = (
       ];
 };
path: src/utils/resources/bootableresources/helpers.ts
user: login: adamviktora
body: This is not related to your changes but can the type of `pvcSources` argument of this `getBootableVolumePVCSource` function be changed to `NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>`?

Also I am getting a type error for the `bootableVolume` returned. I guess it is safe to return the `IoK8sApiCoreV1PersistentVolumeClaim` type because of the `isBootableVolumePVCKind` check - if so, can we alias it to `(bootableVolume as IoK8sApiCoreV1PersistentVolumeClaim)`?
created_at: 2025-05-21T14:18:20Z
author_association: MEMBER
diff_hunk: @@ -69,14 +74,16 @@ const BootableVolumeRow: FC<BootableVolumeRowProps> = ({
   const bootVolumeName = getName(bootableVolume);
   const bootVolumeNamespace = getNamespace(bootableVolume);
   const sizeData = formatBytes(
-    pvcSource?.spec?.resources?.requests?.storage || getVolumeSnapshotSize(volumeSnapshotSource),
+    getDataVolumeSize(dvSource) ||
+      getPVCSize(pvcSource) ||
+      getVolumeSnapshotSize(volumeSnapshotSource),
path: src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx
user: login: pcbailey
body: Thanks for catching that! I had already forgotten about it. 😆 
created_at: 2025-05-28T12:25:15Z
author_association: MEMBER
diff_hunk: @@ -29,11 +33,10 @@ export type UseInstanceTypeAndPreferencesValues = {
 export type UseBootableVolumesValues = {
   bootableVolumes: BootableVolume[];
   dataImportCrons: V1beta1DataImportCron[];
+  dvSources: NamespacedResourceMap<V1beta1DataVolume>;
   error: Error;
   loaded: boolean;
-  pvcSources: {
-    [resourceKeyName: string]: IoK8sApiCoreV1PersistentVolumeClaim;
-  };
+  pvcSources: NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>;
path: src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts
user: login: pcbailey
body: You're right that the initial type was wrong and this fixes it. I didn't observe any issues, but I'm a little surprised that no errors were thrown before this since the type was incorrect.
created_at: 2025-05-28T12:26:54Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,11 @@ export const getBootableVolumePVCSource = (
       ];
 };
path: src/utils/resources/bootableresources/helpers.ts
user: login: pcbailey
body: We can't use `NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>` since that would be `{ [namespace: string]: { [name: string]: A } }`, but `ResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>` should work.
created_at: 2025-05-28T12:30:38Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,11 @@ export const getBootableVolumePVCSource = (
       ];
 };
path: src/utils/resources/bootableresources/helpers.ts
user: login: adamviktora
body: Yeah that's what I am concerned about, because it looks like the way `pvcSources` are used here:
```
pvcSources?
.[(bootableVolume as V1beta1DataSource)?.spec?.source?.pvc?.namespace]?
.[(bootableVolume as V1beta1DataSource)?.spec?.source?.pvc?.name]
```

it expects a different type


created_at: 2025-05-28T13:11:44Z
author_association: MEMBER
diff_hunk: @@ -43,6 +43,11 @@ export const getBootableVolumePVCSource = (
       ];
 };
path: src/utils/resources/bootableresources/helpers.ts
user: login: pcbailey
body: Sorry, taking another look at this I'm wrong.`pvcSources` should be typed as `NamespacedResourceMap<IoK8sApiCoreV1PersistentVolumeClaim>`.

We should create some selectors to get the DataSource name and namespace and replace those. That's pretty ugly as it is.
created_at: 2025-05-28T13:22:53Z
author_association: MEMBER

PR comments:
user: login: openshift-ci-robot
created_at: 2025-05-20T20:08:07Z
body: @pcbailey: This pull request references [CNV-57330](https://issues.redhat.com//browse/CNV-57330) which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.20.0" version, but no target version was set.

<details>

In response to [this](https://github.com/kubevirt-ui/kubevirt-plugin/pull/2679):

>## 📝 Description
>
>This PR updates the Instance Types VM creation flow logic to use the size of the DataVolume over that of the PVC if it exists. The size of PVCs created using a filesystem-based storage class have already been increased to include overhead, so using the already increased size would result in double the overhead.
>
>Jira: https://issues.redhat.com/browse/CNV-57330
>
>## 🎥 Screenshots
>
>### Before
>
>![use-dv-size-for-instancetypes-vm-creation--BEFORE--2025-05-20_11-58](https://github.com/user-attachments/assets/5b4e0326-5a06-43c7-8f5b-52bd5485439c)
>
>### After
>
>![use-dv-size-for-instancetypes-vm-creation--AFTER--2025-05-20_11-45](https://github.com/user-attachments/assets/b6a2ba98-b6a4-4b2b-815f-4bafc09f8020)


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=kubevirt-ui%2Fkubevirt-plugin).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: COLLABORATOR
user: login: openshift-ci[bot]
created_at: 2025-05-28T15:05:42Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubevirt-ui/kubevirt-plugin/pull/2679#pullrequestreview-2875474056" title="Approved">adamviktora</a>*, *<a href="https://github.com/kubevirt-ui/kubevirt-plugin/pull/2679#" title="Author self-approved">pcbailey</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubevirt-ui%2Fkubevirt-plugin).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubevirt-ui/kubevirt-plugin/blob/main/OWNERS)~~ [adamviktora,pcbailey]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR