Skip to content

Commit c12d4b5

Browse files
mergify[bot]Vicente-Chenga110605
authored
feat: support advance option and creation from DataVolume (#776) (#777)
* feat: support advance option and creation from DataVolume - advance option would let user change the accessMode/volumeMode - creation from DataVolume could supports various scenario with 3rd-party storage * feat: add data migration action on volume page * refactor: use show advanced options link instead of checkbox * feat: add feature flag * feat: add feature flag for dataMigration action --------- (cherry picked from commit 566e79e) Signed-off-by: Vicente Cheng <freeze.bilsted@gmail.com> Signed-off-by: Andy Lee <andy.lee@suse.com> Co-authored-by: freeze <1615081+Vicente-Cheng@users.noreply.github.com> Co-authored-by: Andy Lee <andy.lee@suse.com>
1 parent dc91be1 commit c12d4b5

8 files changed

Lines changed: 372 additions & 20 deletions

File tree

pkg/harvester/config/feature-flags.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const FEATURE_FLAGS = {
6565
'vGPUAsPCIDevice',
6666
'instanceManagerResourcesSetting',
6767
'rwxNetworkSetting',
68+
'createPVCWithDataVolume'
6869
],
6970
};
7071

pkg/harvester/config/labels-annotations.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,5 @@ export const HCI = {
8080
VOLUME_SNAPSHOT_CLASS: 'cdi.harvesterhci.io/storageProfileVolumeSnapshotClass',
8181
MAC_ADDRESS: 'harvesterhci.io/mac-address',
8282
NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map',
83+
CDI_POPULATOR_KIND: 'cdi.kubevirt.io/storage.populator.kind',
8384
};

pkg/harvester/config/types.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,8 @@ export const VMIMPORT_SOURCE_KINDS = {
4141
OPENSTACK: 'OpenstackSource',
4242
OVA: 'OvaSource',
4343
};
44+
45+
export const CDI_POPULATOR_KIND = {
46+
VOLUME_IMPORT_SOURCE: 'VolumeImportSource',
47+
VOLUME_CLONE_SOURCE: 'VolumeCloneSource',
48+
};
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<script>
2+
import { mapGetters } from 'vuex';
3+
4+
import { STORAGE_CLASS } from '@shell/config/types';
5+
import { exceptionToErrorsArray } from '@shell/utils/error';
6+
import { sortBy } from '@shell/utils/sort';
7+
import { isInternalStorageClass } from '../utils/storage-class';
8+
9+
import { Card } from '@components/Card';
10+
import { Banner } from '@components/Banner';
11+
import { LabeledInput } from '@components/Form/LabeledInput';
12+
import AsyncButton from '@shell/components/AsyncButton';
13+
import LabeledSelect from '@shell/components/form/LabeledSelect';
14+
15+
export default {
16+
name: 'HarvesterDataMigrationDialog',
17+
18+
emits: ['close'],
19+
20+
components: {
21+
AsyncButton, Banner, Card, LabeledInput, LabeledSelect
22+
},
23+
24+
props: {
25+
resources: {
26+
type: Array,
27+
required: true
28+
}
29+
},
30+
31+
async fetch() {
32+
this.storageClasses = await this.$store.dispatch('harvester/findAll', { type: STORAGE_CLASS });
33+
},
34+
35+
data() {
36+
return {
37+
targetVolumeName: '',
38+
targetStorageClassName: '',
39+
errors: [],
40+
storageClasses: [],
41+
};
42+
},
43+
44+
computed: {
45+
...mapGetters({ t: 'i18n/t' }),
46+
47+
actionResource() {
48+
return this.resources[0];
49+
},
50+
51+
storageClassOptions() {
52+
return sortBy(
53+
this.storageClasses
54+
.filter((sc) => !isInternalStorageClass(sc.metadata?.name))
55+
.map((sc) => ({
56+
label: sc.metadata?.name,
57+
value: sc.metadata?.name
58+
})),
59+
'label'
60+
);
61+
},
62+
63+
disableSave() {
64+
return !this.targetVolumeName || !this.targetStorageClassName;
65+
},
66+
},
67+
68+
methods: {
69+
close() {
70+
this.targetVolumeName = '';
71+
this.targetStorageClassName = '';
72+
this.errors = [];
73+
this.$emit('close');
74+
},
75+
76+
async apply(buttonDone) {
77+
if (!this.actionResource) {
78+
buttonDone(false);
79+
80+
return;
81+
}
82+
83+
if (!this.targetVolumeName) {
84+
const name = this.t('harvester.modal.dataMigration.fields.targetVolumeName.label');
85+
86+
this['errors'] = [this.t('validation.required', { key: name })];
87+
buttonDone(false);
88+
89+
return;
90+
}
91+
92+
if (!this.targetStorageClassName) {
93+
const name = this.t('harvester.modal.dataMigration.fields.targetStorageClassName.label');
94+
95+
this['errors'] = [this.t('validation.required', { key: name })];
96+
buttonDone(false);
97+
98+
return;
99+
}
100+
101+
try {
102+
await this.actionResource.doAction('dataMigration', {
103+
targetVolumeName: this.targetVolumeName,
104+
targetStorageClassName: this.targetStorageClassName
105+
}, {}, false);
106+
107+
buttonDone(true);
108+
this.close();
109+
} catch (err) {
110+
const error = err?.data || err;
111+
112+
this['errors'] = exceptionToErrorsArray(error);
113+
buttonDone(false);
114+
}
115+
},
116+
}
117+
};
118+
</script>
119+
120+
<template>
121+
<Card :show-highlight-border="false">
122+
<template #title>
123+
{{ t('harvester.modal.dataMigration.title') }}
124+
</template>
125+
126+
<template #body>
127+
<LabeledInput
128+
v-model:value="targetVolumeName"
129+
:label="t('harvester.modal.dataMigration.fields.targetVolumeName.label')"
130+
:placeholder="t('harvester.modal.dataMigration.fields.targetVolumeName.placeholder')"
131+
class="mb-20"
132+
required
133+
/>
134+
135+
<LabeledSelect
136+
v-model:value="targetStorageClassName"
137+
:label="t('harvester.modal.dataMigration.fields.targetStorageClassName.label')"
138+
:placeholder="t('harvester.modal.dataMigration.fields.targetStorageClassName.placeholder')"
139+
:options="storageClassOptions"
140+
required
141+
/>
142+
143+
<Banner
144+
v-for="(err, i) in errors"
145+
:key="i"
146+
color="error"
147+
:label="err"
148+
/>
149+
</template>
150+
151+
<template
152+
#actions
153+
class="actions"
154+
>
155+
<div class="buttons">
156+
<button
157+
class="btn role-secondary mr-10"
158+
@click="close"
159+
>
160+
{{ t('generic.cancel') }}
161+
</button>
162+
163+
<AsyncButton
164+
mode="apply"
165+
:disabled="disableSave"
166+
@click="apply"
167+
/>
168+
</div>
169+
</template>
170+
</Card>
171+
</template>
172+
173+
<style lang="scss" scoped>
174+
.actions {
175+
width: 100%;
176+
}
177+
178+
.buttons {
179+
display: flex;
180+
justify-content: flex-end;
181+
width: 100%;
182+
}
183+
</style>

0 commit comments

Comments
 (0)