blob: 5eed42a736dfb4d9b5b8eab5b2fa12b252d0a316 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="snapshot-layout">
<div v-if="!isVolumeResource" class="volume-selection">
<a-form layout="vertical">
<a-form-item :label="$t('label.volume')" required>
<a-select
v-model:value="selectedVolumeId"
:placeholder="$t('label.select.volume')"
:loading="volumesLoading"
show-search
:filter-option="filterOption"
@change="onVolumeChange"
>
<a-select-option
v-for="volume in volumes"
:key="volume.id"
:value="volume.id"
>
{{ volume.name }} ({{ volume.sizegb }}GB)
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<a-tabs v-if="currentVolumeResource && currentVolumeResource.id" defaultActiveKey="1" :animated="false">
<a-tab-pane :tab="$t('label.schedule')" key="1">
<FormSchedule
:loading="loading"
:resource="currentVolumeResource"
:dataSource="dataSource"
:resourceType="'Volume'"
@close-action="closeAction"
@refresh="handleRefresh"/>
</a-tab-pane>
<a-tab-pane :tab="$t('label.action.recurring.snapshot')" key="2">
<ScheduledSnapshots
:loading="loading"
:resource="currentVolumeResource"
:dataSource="dataSource"
@refresh="handleRefresh"
@close-action="closeAction"/>
</a-tab-pane>
</a-tabs>
<div v-if="!currentVolumeResource || !currentVolumeResource.id" class="no-volume-selected">
<div class="empty-state">
<p>{{ $t('message.select.volume.to.continue') }}</p>
</div>
</div>
</div>
</template>
<script>
import { getAPI } from '@/api'
import FormSchedule from '@/views/storage/FormSchedule'
import ScheduledSnapshots from '@/views/storage/ScheduledSnapshots'
export default {
name: 'RecurringSnapshotVolume',
components: {
FormSchedule,
ScheduledSnapshots
},
props: {
resource: {
type: Object,
required: false,
default: () => null
}
},
inject: ['parentFetchData'],
data () {
return {
loading: false,
dataSource: [],
volumes: [],
volumesLoading: false,
selectedVolumeId: null,
selectedVolume: null
}
},
computed: {
resourceType () {
if (!this.resource) return 'none'
if (this.resource.type === 'ROOT' || this.resource.type === 'DATADISK' ||
this.resource.state === 'Ready' || this.resource.state === 'Allocated' ||
this.resource.sizegb !== undefined) {
return 'volume'
}
if (this.resource.intervaltype !== undefined || this.resource.schedule !== undefined) {
return 'snapshotpolicy'
}
return 'unknown'
},
isVolumeResource () {
return this.resourceType === 'volume'
},
currentVolumeResource () {
if (this.isVolumeResource) {
return this.resource
} else {
return this.selectedVolume
}
}
},
created () {
if (this.isVolumeResource) {
this.fetchData()
} else {
this.fetchVolumes()
}
},
methods: {
async fetchVolumes () {
this.volumesLoading = true
try {
const response = await getAPI('listVolumes', { listAll: true })
const volumes = response.listvolumesresponse.volume || []
this.volumes = volumes.filter(volume => {
return volume.state === 'Ready' &&
(volume.hypervisor !== 'KVM' ||
(['Stopped', 'Destroyed'].includes(volume.vmstate)) ||
(this.$store.getters.features.kvmsnapshotenabled))
})
} catch (error) {
this.$message.error(this.$t('message.error.fetch.volumes'))
console.error('Error fetching volumes:', error)
} finally {
this.volumesLoading = false
}
},
onVolumeChange (volumeId) {
const volume = this.volumes.find(v => v.id === volumeId)
if (volume) {
this.selectedVolume = volume
this.selectedVolumeId = volumeId
this.dataSource = []
this.fetchData()
}
},
fetchData () {
const volumeResource = this.currentVolumeResource
if (!volumeResource || !volumeResource.id) {
return
}
const params = {
volumeid: volumeResource.id,
listAll: true
}
this.dataSource = []
this.loading = true
getAPI('listSnapshotPolicies', params).then(json => {
this.loading = false
const listSnapshotPolicies = json.listsnapshotpoliciesresponse.snapshotpolicy
if (listSnapshotPolicies && listSnapshotPolicies.length > 0) {
this.dataSource = listSnapshotPolicies
}
}).catch(error => {
this.loading = false
this.$message.error(this.$t('message.error.fetch.snapshot.policies'))
console.error('Error fetching snapshot policies:', error)
})
},
handleRefresh () {
this.fetchData()
this.parentFetchData()
},
closeAction () {
this.fetchData()
this.$emit('close-action')
},
filterOption (input, option) {
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
</script>
<style lang="less" scoped>
.snapshot-layout {
max-width: 800px;
.volume-selection {
margin-bottom: 20px;
padding: 16px;
border: 1px solid #d9d9d9;
border-radius: 6px;
background-color: #fafafa;
.ant-form-item {
margin-bottom: 0;
.ant-select {
width: 100%;
min-width: 400px;
}
}
}
.current-volume-info {
margin-bottom: 16px;
}
.no-volume-selected {
text-align: center;
padding: 40px 20px;
}
}
</style>