You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/08/20 08:44:39 UTC
[cloudstack-primate] branch master updated: storage: Allow creating
snapshot from VM snapshot (#587)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new bbd4fdd storage: Allow creating snapshot from VM snapshot (#587)
bbd4fdd is described below
commit bbd4fddefdba3726f16b79b070749a90cad8a71b
Author: Rakesh <ra...@gmail.com>
AuthorDate: Thu Aug 20 10:44:24 2020 +0200
storage: Allow creating snapshot from VM snapshot (#587)
In old UI we support creating snapshot from
VM snapshot by selecting one of the volume
belonging to VM. Allow the same in new UI
Co-authored-by: Rakesh Venkatesh <r....@global.leaseweb.com>
---
src/config/section/storage.js | 13 +-
src/locales/en.json | 5 +
src/views/storage/CreateSnapshotFromVMSnapshot.vue | 151 +++++++++++++++++++++
3 files changed, 168 insertions(+), 1 deletion(-)
diff --git a/src/config/section/storage.js b/src/config/section/storage.js
index a18401b..2e7c205 100644
--- a/src/config/section/storage.js
+++ b/src/config/section/storage.js
@@ -256,6 +256,7 @@ export default {
columns: () => {
var fields = ['name', 'state', 'volumename', 'intervaltype', 'created']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
+ fields.push('domain')
fields.push('account')
}
return fields
@@ -315,7 +316,7 @@ export default {
permission: ['listVMSnapshot'],
resourceType: 'VMSnapshot',
columns: () => {
- var fields = ['displayname', 'state', 'name', 'type', 'current', 'parentName', 'created']
+ const fields = ['displayname', 'state', 'name', 'type', 'current', 'parentName', 'created']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
fields.push('domain')
fields.push('account')
@@ -326,6 +327,16 @@ export default {
searchFilters: ['name', 'domainid', 'account', 'tags'],
actions: [
{
+ api: 'createSnapshotFromVMSnapshot',
+ icon: 'camera',
+ label: 'label.action.create.snapshot.from.vmsnapshot',
+ message: 'message.action.create.snapshot.from.vmsnapshot',
+ dataView: true,
+ popup: true,
+ show: (record) => { return (record.state === 'Ready' && record.hypervisor === 'KVM') },
+ component: () => import('@/views/storage/CreateSnapshotFromVMSnapshot.vue')
+ },
+ {
api: 'revertToVMSnapshot',
icon: 'sync',
label: 'label.action.vmsnapshot.revert',
diff --git a/src/locales/en.json b/src/locales/en.json
index 63cbba7..2566cab 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -83,6 +83,7 @@
"label.action.create.template.from.vm": "Create Template from VM",
"label.action.create.template.from.volume": "Create Template from Volume",
"label.action.create.template.processing": "Creating Template....",
+"label.action.create.snapshot.from.vmsnapshot": "Create Snapshot from VM Snapshot",
"label.action.create.vm": "Create VM",
"label.action.create.vm.processing": "Creating VM....",
"label.action.create.volume": "Create Volume",
@@ -2341,6 +2342,7 @@
"message.action.cancel.maintenance.mode": "Please confirm that you want to cancel this maintenance.",
"message.action.change.service.warning.for.instance": "Your instance must be stopped before attempting to change its current service offering.",
"message.action.change.service.warning.for.router": "Your router must be stopped before attempting to change its current service offering.",
+"message.action.create.snapshot.from.vmsnapshot": "Please confirm that you want to create snapshot from VM Snapshot",
"message.action.delete.backup.offering": "Please confirm that you want to delete this backup offering?",
"message.action.delete.cluster": "Please confirm that you want to delete this cluster.",
"message.action.delete.disk.offering": "Please confirm that you want to delete this disk offering.",
@@ -2595,6 +2597,8 @@
"message.create.internallb.failed": "Failed to create Internal LB",
"message.create.internallb.processing": "Creation of Internal LB is in progress",
"message.create.service.offering": "Service offering created",
+"message.create.snapshot.from.vmsnapshot.failed": "Failed to create Snapshot from VM Snapshot",
+"message.create.snapshot.from.vmsnapshot.progress": "Snapshot Creation in progress",
"message.create.template": "Are you sure you want to create template?",
"message.create.template.vm": "Create VM from template <b id=\"p_name\"></b>",
"message.create.template.volume": "Please specify the following information before creating a template of your disk volume: <b><span id=\"volume_name\"></span></b>. Creation of the template can range from several minutes to longer depending on the size of the volume.",
@@ -3064,6 +3068,7 @@
"message.success.create.keypair": "Successfully created SSH key pair",
"message.success.create.kubernetes.cluter": "Successfully created Kubernetes cluster",
"message.success.create.l2.network": "Successfully created L2 network",
+"message.success.create.snapshot.from.vmsnapshot": "Successfully created Snapshot from VM snapshot",
"message.success.create.volume": "Successfully created volume",
"message.success.delete": "Delete success",
"message.success.delete.acl.rule": "Successfully removed ACL rule",
diff --git a/src/views/storage/CreateSnapshotFromVMSnapshot.vue b/src/views/storage/CreateSnapshotFromVMSnapshot.vue
new file mode 100644
index 0000000..6e3be3c
--- /dev/null
+++ b/src/views/storage/CreateSnapshotFromVMSnapshot.vue
@@ -0,0 +1,151 @@
+// 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>
+ <a-spin :spinning="loading">
+ <a-form class="form" :form="form" @submit="handleSubmit" layout="vertical">
+ <a-form-item :label="$t('label.name')">
+ <a-input
+ v-decorator="['name', {
+ rules: [{ required: true, message: $t('message.error.name') }]
+ }]"
+ :placeholder="$t('label.snapshot.name')"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.volume')">
+ <a-select
+ v-decorator="['volumeid', {
+ initialValue: selectedVolumeId,
+ rules: [{ required: true, message: $t('message.error.select') }]}]"
+ :loading="loading"
+ @change="id => (volumes.filter(x => x.id === id))"
+ >
+ <a-select-option
+ v-for="(volume, index) in volumes"
+ :value="volume.id"
+ :key="index">
+ {{ volume.displaytext || volume.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ <div :span="24" class="action-button">
+ <a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
+ <a-button type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
+ </div>
+ </a-form>
+ </a-spin>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+ name: 'CreateSnapshotFromVMSnapshot',
+ props: {
+ resource: {
+ type: Object,
+ required: true
+ }
+ },
+ data () {
+ return {
+ volumes: [],
+ selectedVolumeId: '',
+ loading: false
+ }
+ },
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ },
+ mounted () {
+ this.fetchData()
+ },
+ methods: {
+ fetchData () {
+ this.loading = true
+ api('listVolumes', {
+ virtualmachineid: this.resource.virtualmachineid,
+ listall: true
+ }).then(json => {
+ this.volumes = json.listvolumesresponse.volume || []
+ this.selectedVolumeId = this.volumes[0].id || ''
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ handleSubmit (e) {
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+ this.loading = true
+ api('createSnapshotFromVMSnapshot', {
+ name: values.name,
+ volumeid: values.volumeid,
+ vmsnapshotid: this.resource.id
+ }).then(response => {
+ this.$pollJob({
+ jobId: response.createsnapshotfromvmsnapshotresponse.jobid,
+ successMessage: this.$t('message.success.create.snapshot.from.vmsnapshot'),
+ successMethod: () => {
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('message.success.create.snapshot.from.vmsnapshot'),
+ jobid: response.createsnapshotfromvmsnapshotresponse.jobid,
+ description: values.name,
+ status: 'progress'
+ })
+ this.$emit('refresh-data')
+ },
+ errorMessage: this.$t('message.create.snapshot.from.vmsnapshot.failed'),
+ errorMethod: () => {
+ this.$emit('refresh-data')
+ },
+ loadingMessage: this.$t('message.create.snapshot.from.vmsnapshot.progress'),
+ catchMessage: this.$t('error.fetching.async.job.result')
+ })
+ }).catch(error => {
+ this.$notifyError(error)
+ }).finally(() => {
+ this.loading = false
+ this.$emit('refresh-data')
+ this.closeModal()
+ })
+ })
+ },
+ closeModal () {
+ this.$emit('close-action')
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.form {
+ width: 80vw;
+
+ @media (min-width: 500px) {
+ width: 400px;
+ }
+}
+
+.action-button {
+ text-align: right;
+
+ button {
+ margin-right: 5px;
+ }
+}
+</style>