Skip to content

Commit 7b9a1b4

Browse files
committed
add create policy - snap/backup in the list view with resource (volume/vm) selection
1 parent a759acc commit 7b9a1b4

File tree

5 files changed

+371
-16
lines changed

5 files changed

+371
-16
lines changed

ui/public/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878
"label.action.copy.iso": "Copy ISO",
7979
"label.action.copy.snapshot": "Copy Snapshot",
8080
"label.action.copy.template": "Copy Template",
81+
"label.action.create.backup.schedule": "Create Backup Schedule",
82+
"label.action.create.recurring.snapshot": "Create Recurring Snapshot",
8183
"label.action.create.snapshot.from.vmsnapshot": "Create Snapshot from Instance Snapshot",
8284
"label.action.create.template.from.volume": "Create Template from volume",
8385
"label.action.create.volume": "Create Volume",
@@ -1499,7 +1501,7 @@
14991501
"label.max.primary.storage": "Max. primary (GiB)",
15001502
"label.max.secondary.storage": "Max. secondary (GiB)",
15011503
"label.max.migrations": "Max. migrations",
1502-
"label.maxbackup": "Max. Backups",
1504+
"label.maxbackups": "Max. Backups",
15031505
"label.maxbackupstorage": "Max. Backup Storage (GiB)",
15041506
"label.maxbackups.to.retain": "Max. Backups to retain",
15051507
"label.maxbucket": "Max. Buckets",
@@ -3711,6 +3713,7 @@
37113713
"message.select.security.groups": "Please select security group(s) for your new Instance.",
37123714
"message.select.start.date.and.time": "Select a start date & time.",
37133715
"message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v",
3716+
"message.select.volume.to.continue": "Please select a volume to continue.",
37143717
"message.select.zone.description": "Select type of Zone basic/advanced.",
37153718
"message.select.zone.hint": "This is the type of Zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.",
37163719
"message.server": "Server : ",

ui/src/config/section/storage.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,21 @@ export default {
427427
},
428428
searchFilters: ['volumeid'],
429429
actions: [
430+
{
431+
api: 'createSnapshotPolicy',
432+
icon: 'plus-outlined',
433+
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
434+
label: 'label.action.create.recurring.snapshot',
435+
listView: true,
436+
show: () => { return 'createSnapshotPolicy' in store.getters.apis },
437+
popup: true,
438+
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/RecurringSnapshotVolume.vue'))),
439+
mapping: {
440+
intervaltype: {
441+
options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY']
442+
}
443+
}
444+
},
430445
{
431446
api: 'deleteSnapshotPolicies',
432447
icon: 'delete-outlined',
@@ -541,6 +556,21 @@ export default {
541556
},
542557
searchFilters: ['virtualmachineid'],
543558
actions: [
559+
{
560+
api: 'createBackupSchedule',
561+
icon: 'plus-outlined',
562+
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
563+
label: 'label.action.create.backup.schedule',
564+
listView: true,
565+
show: () => { return 'createBackupSchedule' in store.getters.apis },
566+
popup: true,
567+
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/backup/CreateBackupSchedule.vue'))),
568+
mapping: {
569+
intervaltype: {
570+
options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY']
571+
}
572+
}
573+
},
544574
{
545575
api: 'deleteBackupSchedule',
546576
icon: 'delete-outlined',

ui/src/views/compute/BackupScheduleWizard.vue

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
<FormSchedule
2323
:loading="loading"
2424
:resource="resource"
25-
:dataSource="dataSource"/>
25+
:dataSource="dataSource"
26+
@close-action="closeAction"
27+
@refresh="handleRefresh"/>
2628
</a-tab-pane>
2729
<a-tab-pane :tab="$t('label.scheduled.backups')" key="2">
2830
<BackupSchedule
2931
:loading="loading"
3032
:resource="resource"
31-
:dataSource="dataSource" />
33+
:dataSource="dataSource"
34+
@refresh="handleRefresh"
35+
@close-action="closeAction" />
3236
</a-tab-pane>
3337
</a-tabs>
3438
</div>
@@ -54,7 +58,7 @@ export default {
5458
data () {
5559
return {
5660
loading: false,
57-
dataSource: {}
61+
dataSource: []
5862
}
5963
},
6064
provide () {
@@ -69,16 +73,21 @@ export default {
6973
methods: {
7074
fetchData () {
7175
const params = {}
72-
this.dataSource = {}
76+
this.dataSource = []
7377
this.loading = true
7478
params.virtualmachineid = this.resource.id
7579
getAPI('listBackupSchedule', params).then(json => {
76-
this.dataSource = json.listbackupscheduleresponse.backupschedule || {}
80+
this.dataSource = json.listbackupscheduleresponse.backupschedule || []
7781
}).finally(() => {
7882
this.loading = false
7983
})
8084
},
85+
handleRefresh () {
86+
this.fetchData()
87+
this.$emit('refresh')
88+
},
8189
closeAction () {
90+
this.$emit('refresh')
8291
this.$emit('close-action')
8392
}
8493
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
<template>
19+
<div class="create-backup-schedule-layout">
20+
<div v-if="!isVMResource" class="vm-selection">
21+
<a-form layout="vertical">
22+
<a-form-item :label="$t('label.virtualmachine')" required>
23+
<a-select
24+
v-model:value="selectedVMId"
25+
:placeholder="$t('label.select.virtualmachine')"
26+
:loading="vmsLoading"
27+
show-search
28+
:filter-option="filterOption"
29+
@change="onVMChange"
30+
>
31+
<a-select-option
32+
v-for="vm in vms"
33+
:key="vm.id"
34+
:value="vm.id"
35+
>
36+
{{ vm.name }} ({{ vm.account }})
37+
</a-select-option>
38+
</a-select>
39+
</a-form-item>
40+
</a-form>
41+
</div>
42+
<div v-if="isVMResource" class="current-vm-info">
43+
<a-alert
44+
:message="`${$t('label.backup.schedules.for')} ${resource.name}`"
45+
type="info"
46+
show-icon
47+
style="margin-bottom: 16px"
48+
/>
49+
</div>
50+
<div v-if="currentVMResource && currentVMResource.id">
51+
<BackupScheduleWizard
52+
ref="backupScheduleWizard"
53+
:resource="currentVMResource"
54+
@close-action="closeAction"
55+
@refresh="handleRefresh"
56+
/>
57+
</div>
58+
<div v-if="!currentVMResource || !currentVMResource.id" class="no-vm-selected">
59+
<div class="empty-state">
60+
<p>{{ $t('message.select.vm.to.continue') }}</p>
61+
</div>
62+
</div>
63+
</div>
64+
</template>
65+
66+
<script>
67+
import { getAPI } from '@/api'
68+
import BackupScheduleWizard from '@/views/compute/BackupScheduleWizard'
69+
70+
export default {
71+
name: 'CreateBackupSchedule',
72+
components: {
73+
BackupScheduleWizard
74+
},
75+
props: {
76+
resource: {
77+
type: Object,
78+
required: false,
79+
default: () => null
80+
}
81+
},
82+
data () {
83+
return {
84+
vms: [],
85+
vmsLoading: false,
86+
selectedVMId: null,
87+
selectedVM: null
88+
}
89+
},
90+
computed: {
91+
resourceType () {
92+
if (!this.resource) return 'none'
93+
if (this.resource.vmstate !== undefined ||
94+
this.resource.guestosid !== undefined || this.resource.hypervisor !== undefined ||
95+
this.resource.backupofferingid !== undefined) {
96+
return 'vm'
97+
}
98+
if (this.resource.intervaltype !== undefined && this.resource.schedule !== undefined) {
99+
return 'backupschedule'
100+
}
101+
return 'unknown'
102+
},
103+
isVMResource () {
104+
return this.resourceType === 'vm'
105+
},
106+
currentVMResource () {
107+
if (this.isVMResource) {
108+
return this.resource
109+
} else {
110+
return this.selectedVM
111+
}
112+
}
113+
},
114+
created () {
115+
if (!this.isVMResource) {
116+
this.fetchVMs()
117+
}
118+
},
119+
methods: {
120+
async fetchVMs () {
121+
this.vmsLoading = true
122+
try {
123+
const response = await getAPI('listVirtualMachines', { listAll: true })
124+
const vms = response.listvirtualmachinesresponse.virtualmachine || []
125+
this.vms = vms.filter(vm => {
126+
return vm.backupofferingid && ['Running', 'Stopped'].includes(vm.state)
127+
})
128+
} catch (error) {
129+
this.$message.error(this.$t('message.error.fetch.vms'))
130+
console.error('Error fetching VMs:', error)
131+
} finally {
132+
this.vmsLoading = false
133+
}
134+
},
135+
onVMChange (vmId) {
136+
const vm = this.vms.find(v => v.id === vmId)
137+
if (vm) {
138+
this.selectedVM = vm
139+
this.selectedVMId = vmId
140+
}
141+
},
142+
closeAction () {
143+
this.$emit('refresh')
144+
this.$emit('close-action')
145+
},
146+
handleRefresh () {
147+
this.$emit('refresh')
148+
},
149+
filterOption (input, option) {
150+
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
151+
}
152+
}
153+
}
154+
</script>
155+
156+
<style lang="less" scoped>
157+
.create-backup-schedule-layout {
158+
.vm-selection {
159+
margin-bottom: 20px;
160+
padding: 16px;
161+
border: 1px solid #d9d9d9;
162+
border-radius: 6px;
163+
background-color: #fafafa;
164+
.ant-form-item {
165+
margin-bottom: 0;
166+
.ant-select {
167+
width: 100%;
168+
min-width: 400px;
169+
}
170+
}
171+
}
172+
173+
.current-vm-info {
174+
margin-bottom: 16px;
175+
}
176+
177+
.no-vm-selected {
178+
text-align: center;
179+
padding: 40px 20px;
180+
}
181+
}
182+
</style>

0 commit comments

Comments
 (0)