show volume encryption in vm detail volume tab

Signed-off-by: andy.lee <andy.lee@suse.com>
This commit is contained in:
andy.lee 2024-09-20 15:13:42 +08:00 committed by Francesco Torchia
parent 958e97f769
commit db21093670
No known key found for this signature in database
GPG Key ID: E6D011B7415D4393
8 changed files with 90 additions and 37 deletions

View File

@ -114,7 +114,7 @@ export default {
<div <div
data-testid="input-hec-bus" data-testid="input-hec-bus"
class="col span-3" class="col span-6"
> >
<InputOrDisplay <InputOrDisplay
:name="t('harvester.virtualMachine.volume.bus')" :name="t('harvester.virtualMachine.volume.bus')"

View File

@ -8,6 +8,9 @@ import { PVC } from '@shell/config/types';
import { formatSi, parseSi } from '@shell/utils/units'; import { formatSi, parseSi } from '@shell/utils/units';
import { HCI } from '../../../../types'; import { HCI } from '../../../../types';
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map'; import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
import { _VIEW } from '@shell/config/query-params';
import LabelValue from '@shell/components/LabelValue';
import { ucFirst } from '@shell/utils/string';
export default { export default {
name: 'HarvesterEditVMImage', name: 'HarvesterEditVMImage',
@ -15,7 +18,7 @@ export default {
emits: ['update'], emits: ['update'],
components: { components: {
UnitInput, LabeledInput, LabeledSelect, InputOrDisplay UnitInput, LabeledInput, LabeledSelect, InputOrDisplay, LabelValue
}, },
props: { props: {
@ -75,6 +78,14 @@ export default {
}, },
computed: { computed: {
encryptionValue() {
return ucFirst(String(this.value.isEncrypted));
},
isView() {
return this.mode === _VIEW;
},
imagesOption() { imagesOption() {
return this.images.filter(c => c.isReady).sort((a, b) => a.creationTimestamp > b.creationTimestamp ? -1 : 1).map( (I) => { return this.images.filter(c => c.isReady).sort((a, b) => a.creationTimestamp > b.creationTimestamp ? -1 : 1).map( (I) => {
return { return {
@ -272,7 +283,7 @@ export default {
<div class="row mb-20"> <div class="row mb-20">
<div <div
data-testid="input-hevi-bus" data-testid="input-hevi-bus"
class="col span-3" class="col span-6"
> >
<InputOrDisplay <InputOrDisplay
:name="t('harvester.virtualMachine.volume.bus')" :name="t('harvester.virtualMachine.volume.bus')"
@ -288,6 +299,15 @@ export default {
/> />
</InputOrDisplay> </InputOrDisplay>
</div> </div>
<div
v-if="isView"
class="col span-6"
>
<LabelValue
:name="t('harvester.virtualMachine.volume.encryption')"
:value="encryptionValue"
/>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -8,6 +8,9 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
import { PVC, STORAGE_CLASS } from '@shell/config/types'; import { PVC, STORAGE_CLASS } from '@shell/config/types';
import { formatSi, parseSi } from '@shell/utils/units'; import { formatSi, parseSi } from '@shell/utils/units';
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map'; import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
import { _VIEW } from '@shell/config/query-params';
import LabelValue from '@shell/components/LabelValue';
import { ucFirst } from '@shell/utils/string';
export default { export default {
name: 'HarvesterEditVolume', name: 'HarvesterEditVolume',
@ -15,7 +18,7 @@ export default {
emits: ['update'], emits: ['update'],
components: { components: {
InputOrDisplay, Loading, LabeledInput, LabeledSelect, UnitInput, InputOrDisplay, Loading, LabeledInput, LabeledSelect, UnitInput, LabelValue
}, },
props: { props: {
@ -61,6 +64,13 @@ export default {
}, },
computed: { computed: {
encryptionValue() {
return ucFirst(String(this.value.isEncrypted));
},
isView() {
return this.mode === _VIEW;
},
pvcsResource() { pvcsResource() {
const allPVCs = this.$store.getters['harvester/all'](PVC) || []; const allPVCs = this.$store.getters['harvester/all'](PVC) || [];
@ -215,7 +225,7 @@ export default {
<div class="row mb-20"> <div class="row mb-20">
<div <div
data-testid="input-hev-bus" data-testid="input-hev-bus"
class="col span-3" class="col span-6"
> >
<InputOrDisplay :name="t('harvester.virtualMachine.volume.bus')" :value="value.bus" :mode="mode"> <InputOrDisplay :name="t('harvester.virtualMachine.volume.bus')" :value="value.bus" :mode="mode">
<LabeledSelect <LabeledSelect
@ -228,6 +238,15 @@ export default {
/> />
</InputOrDisplay> </InputOrDisplay>
</div> </div>
<div
v-if="isView"
class="col span-6"
>
<LabelValue
:name="t('harvester.virtualMachine.volume.encryption')"
:value="encryptionValue"
/>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -596,6 +596,10 @@ harvester:
addContainer: Add Container addContainer: Add Container
setFirst: Set as root volume setFirst: Set as root volume
saveVolume: Update Volume saveVolume: Update Volume
encryption: Encryption
lockTooltip:
all: All volumes are encrypted
partial: Partial volumes are encrypted
title: title:
vmImage: Image Volume vmImage: Image Volume
existingVolume: Existing Volume existingVolume: Existing Volume
@ -640,10 +644,6 @@ harvester:
userTips: The user to be added must already exist; otherwise, the credentials will not take effect. userTips: The user to be added must already exist; otherwise, the credentials will not take effect.
duplicatedUser: User already exists. duplicatedUser: User already exists.
invalidUser: Invalid Username. invalidUser: Invalid Username.
lockIconTooltip:
image: Encrypted Image
volume: Encrypted Volume
both: Encrypted Image and Encrypted Volume
input: input:
name: Name name: Name
memory: Memory memory: Memory

View File

@ -173,15 +173,15 @@ export default {
methods: { methods: {
lockIconTooltipMessage(row) { lockIconTooltipMessage(row) {
if (row.isVMImageEncrypted && row.isVolumeEncrypted) { const message = '';
return this.t('harvester.virtualMachine.lockIconTooltip.both');
} else if (row.isVMImageEncrypted) { if (row.encryptedVolumeType === 'all') {
return this.t('harvester.virtualMachine.lockIconTooltip.image'); return this.t('harvester.virtualMachine.volume.lockTooltip.all');
} else if (row.isVolumeEncrypted) { } else if (row.encryptedVolumeType === 'partial') {
return this.t('harvester.virtualMachine.lockIconTooltip.volume'); return this.t('harvester.virtualMachine.volume.lockTooltip.partial');
} }
return ''; return message;
} }
} }
}; };
@ -213,7 +213,12 @@ export default {
:to="scope.row.detailLocation" :to="scope.row.detailLocation"
> >
{{ scope.row.metadata.name }} {{ scope.row.metadata.name }}
<i v-if="lockIconTooltipMessage(scope.row)" v-tooltip="lockIconTooltipMessage(scope.row)" class="icon icon-lock" /> <i
v-if="lockIconTooltipMessage(scope.row)"
v-tooltip="lockIconTooltipMessage(scope.row)"
class="icon icon-lock"
:class="{'green-icon': scope.row.encryptedVolumeType === 'all', 'yellow-icon': scope.row.encryptedVolumeType === 'partial'}"
/>
</router-link> </router-link>
<span v-else> <span v-else>
{{ scope.row.metadata.name }} {{ scope.row.metadata.name }}
@ -234,6 +239,14 @@ export default {
} }
} }
.green-icon {
color: var(--success);
}
.yellow-icon {
color: var(--warning);
}
.name-console { .name-console {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -418,8 +418,10 @@ export default {
let size = '10Gi'; let size = '10Gi';
const imageResource = this.images.find( I => this.imageId === I.id); const imageResource = this.images.find( I => this.imageId === I.id);
const isIsoImage = /iso$/i.test(imageResource?.imageSuffix); const isIsoImage = /iso$/i.test(imageResource?.imageSuffix);
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize); const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
const isEncrypted = imageResource?.isEncrypted || false;
if (isIsoImage) { if (isIsoImage) {
bus = 'sata'; bus = 'sata';
@ -447,6 +449,7 @@ export default {
storageClassName: '', storageClassName: '',
image: this.imageId, image: this.imageId,
volumeMode: 'Block', volumeMode: 'Block',
isEncrypted
}); });
} else { } else {
out = _disks.map( (DISK, index) => { out = _disks.map( (DISK, index) => {
@ -523,7 +526,11 @@ export default {
minExponent: 3, minExponent: 3,
}); });
const volumeStatus = this.pvcs.find(P => P.id === `${ this.value.metadata.namespace }/${ volumeName }`)?.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR]; const pvc = this.pvcs.find(P => P.id === `${ this.value.metadata.namespace }/${ volumeName }`);
const volumeStatus = pvc?.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
const isEncrypted = pvc?.isEncrypted || false;
return { return {
id: randomStr(5), id: randomStr(5),
@ -543,7 +550,8 @@ export default {
hotpluggable, hotpluggable,
volumeStatus, volumeStatus,
dataSource, dataSource,
namespace namespace,
isEncrypted
}; };
}); });
} }

View File

@ -214,7 +214,7 @@ export default class HciPv extends HarvesterResource {
} }
get isEncrypted() { get isEncrypted() {
return this.relatedPV?.spec.csi.volumeAttributes.encrypted || false; return Boolean(this.relatedPV?.spec.csi.volumeAttributes.encrypted) || false;
} }
get longhornVolume() { get longhornVolume() {

View File

@ -574,27 +574,20 @@ export default class VirtVm extends HarvesterResource {
return vmis.find(VMI => VMI.id === this.id); return vmis.find(VMI => VMI.id === this.id);
} }
get isVMImageEncrypted() { get encryptedVolumeType() {
const inStore = this.productInStore;
const imageList = this.$rootGetters[`${ inStore }/all`](HCI.IMAGE);
const rootImage = imageList.find(image => this.rootImageId === image.id);
return rootImage?.isEncrypted || false;
}
get isVolumeEncrypted() {
const inStore = this.productInStore; const inStore = this.productInStore;
const pvcs = this.$rootGetters[`${ inStore }/all`](PVC); const pvcs = this.$rootGetters[`${ inStore }/all`](PVC);
// filter out the root image PVC const volumeClaimNames = this.spec.template.spec.volumes?.map(v => v.persistentVolumeClaim?.claimName).filter(v => !!v) || [];
const nonRootImagePVCs = pvcs.filter(pvc => !pvc.metadata.annotations[HCI_ANNOTATIONS.IMAGE_ID]); const volumes = pvcs.filter(pvc => volumeClaimNames.includes(pvc.metadata.name));
const volumeClaimNames = this.spec.template.spec.volumes?.map(v => v.persistentVolumeClaim?.claimName).map(v => v) || []; if (volumes.every(vol => vol.isEncrypted)) {
return 'all';
const pvcVolumes = nonRootImagePVCs.filter(pvc => volumeClaimNames.includes(pvc.metadata.name)); } else if (volumes.some(vol => vol.isEncrypted)) {
return 'partial';
// if any non-rootImage PCV volume is encrypted, return true, otherwise return false } else {
return pvcVolumes.some(pvcVol => pvcVol.isEncrypted) ; return 'none';
}
} }
get isError() { get isError() {