feat: support manually configuring VM IP (#315)

* feat: add annotations

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>

* feat: map annotation

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>

* feat: add annotaion to template

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>

---------

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
This commit is contained in:
Yiya Chen 2025-06-06 10:03:37 +08:00 committed by GitHub
parent 01528f7429
commit 29b1ab2fb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 86 additions and 10 deletions

View File

@ -68,5 +68,6 @@ export const HCI = {
SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId', SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId',
DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine', DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine',
K8S_ARCH: 'kubernetes.io/arch', K8S_ARCH: 'kubernetes.io/arch',
IMAGE_DISPLAY_NAME: 'harvesterhci.io/imageDisplayName' IMAGE_DISPLAY_NAME: 'harvesterhci.io/imageDisplayName',
CUSTOM_IP: 'harvesterhci.io/custom-ip'
}; };

View File

@ -189,10 +189,16 @@ export default {
} }
this.value.spec['templateId'] = `${ namespace }/${ name }`; this.value.spec['templateId'] = `${ namespace }/${ name }`;
// inherit labels and annotations so the VM gets them when created from the template
this.value.spec.vm.metadata.labels = { this.value.spec.vm.metadata.labels = {
...this.value.spec.vm.metadata.labels, ...this.value.spec.vm.metadata.labels,
...this.value.metadata.labels ...this.value.metadata.labels
}; };
this.value.spec.vm.metadata.annotations = {
...this.value.spec.vm.metadata.annotations,
...this.value.metadata.annotations
};
const res = await this.value.save(); const res = await this.value.save();
await this.saveSecret(res); await this.saveSecret(res);
@ -363,6 +369,26 @@ export default {
@update:value="value.setInstanceLabels($event)" @update:value="value.setInstanceLabels($event)"
/> />
</Tab> </Tab>
<Tab
name="annotations"
:label="t('harvester.tab.annotations')"
:weight="-11"
>
<Banner color="info">
<t k="harvester.virtualMachine.annotations.banner" />
</Banner>
<KeyValue
key="annotations"
:value="value.annotations"
:protected-keys="value.systemAnnotations || []"
:toggle-filter="true"
:add-label="t('labels.addAnnotation')"
:mode="mode"
:read-allowed="false"
:value-can-be-empty="true"
@update:value="value.setAnnotations($event)"
/>
</Tab>
<Tab <Tab
name="advanced" name="advanced"
:label="t('harvester.tab.advanced')" :label="t('harvester.tab.advanced')"

View File

@ -255,9 +255,10 @@ export default {
cloneVersionVM.metadata.annotations[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE] = JSON.stringify(deleteDataSource); cloneVersionVM.metadata.annotations[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE] = JSON.stringify(deleteDataSource);
// Update labels and instance labels value // Update labels, instance labels and annotations
this.value.metadata.labels = cloneVersionVM.metadata.labels; this.value.metadata.labels = cloneVersionVM.metadata.labels;
this.value.spec.template.metadata.labels = cloneVersionVM.spec.template.metadata.labels; this.value.spec.template.metadata.labels = cloneVersionVM.spec.template.metadata.labels;
this.value.metadata.annotations = cloneVersionVM.metadata.annotations;
this.getInitConfig({ this.getInitConfig({
value: cloneVersionVM, existUserData: true, fromTemplate: true value: cloneVersionVM, existUserData: true, fromTemplate: true
@ -764,10 +765,31 @@ export default {
/> />
</Tab> </Tab>
<Tab
name="annotations"
:label="t('harvester.tab.annotations')"
:weight="-11"
>
<Banner color="info">
<t k="harvester.virtualMachine.annotations.banner" />
</Banner>
<KeyValue
key="annotations"
:value="value.annotations"
:protected-keys="value.systemAnnotations || []"
:toggle-filter="toggler"
:add-label="t('labels.addAnnotation')"
:mode="mode"
:read-allowed="false"
:value-can-be-empty="true"
@update:value="value.setAnnotations($event)"
/>
</Tab>
<Tab <Tab
name="advanced" name="advanced"
:label="t('harvester.tab.advanced')" :label="t('harvester.tab.advanced')"
:weight="-11" :weight="-12"
> >
<div class="row mb-20"> <div class="row mb-20">
<div class="col span-6"> <div class="col span-6">

View File

@ -35,11 +35,26 @@ export default {
}, },
computed: { computed: {
// Return VM instance IP and VM annotation IP
ips() { ips() {
return [...this.vmiIp, ...this.networkAnnotationIP] if (this.vmiIp.length) {
.filter(Boolean) return [...this.vmiIp, ...this.networkAnnotationIP]
.sort((a, b) => a.ip < b.ip ? -1 : 1); .filter(Boolean)
.sort((a, b) => a.ip < b.ip ? -1 : 1);
}
return this.customAnnotationIP;
},
customAnnotationIP() {
const annotationIp = get(this.row, `metadata.annotations."${ HCI_ANNOTATIONS.CUSTOM_IP }"`);
if (annotationIp && isIpv4(annotationIp)) {
return [{
name: 'custom-ip', ip: annotationIp, isCustom: true
}];
}
return [];
}, },
networkAnnotationIP() { networkAnnotationIP() {
@ -97,12 +112,13 @@ export default {
<template> <template>
<div v-if="showIP"> <div v-if="showIP">
<span <span
v-for="{ip, name} in ips" v-for="{ ip, name, isCustom } in ips"
:key="ip" :key="`${ip}-${name}`"
> >
<CopyToClipboardText <CopyToClipboardText
v-clean-tooltip="name" v-clean-tooltip="isCustom ? t('harvester.formatters.harvesterIpAddress.customIpTooltip') : name"
:text="ip" :text="ip"
:plain="isCustom"
/> />
</span> </span>
</div> </div>

View File

@ -229,6 +229,8 @@ harvester:
{count, plural, {count, plural,
=1 {core} =1 {core}
other {cores}} other {cores}}
harvesterIpAddress:
customIpTooltip: "Custom IP (set via annotation)"
tableHeaders: tableHeaders:
imageEncryption: Encryption imageEncryption: Encryption
size: Size size: Size
@ -276,6 +278,7 @@ harvester:
quotas: Quotas quotas: Quotas
snapshots: Snapshots snapshots: Snapshots
instanceLabel: Instance Labels instanceLabel: Instance Labels
annotations: Annotations
fields: fields:
version: Version version: Version
name: Name name: Name
@ -792,6 +795,8 @@ harvester:
banner: These labels are automatically synchronized to the virtual machine instance. banner: These labels are automatically synchronized to the virtual machine instance.
labels: labels:
banner: These key values are added as labels to the virtual machine. banner: These key values are added as labels to the virtual machine.
annotations:
banner: These key values are added as annotations to the virtual machine.
volume: volume:
label: Volumes label: Volumes

View File

@ -284,4 +284,10 @@ export default class HciVmTemplateVersion extends HarvesterResource {
get efiPersistentStateFeatureEnabled() { get efiPersistentStateFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState'); return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState');
} }
get systemAnnotations() {
const annotations = this.annotations || {};
return Object.keys(annotations).filter((key) => key.includes(HCI_ANNOTATIONS.TEMPLATE_VERSION_CUSTOM_NAME));
}
} }