1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-25 02:50:08 +03:00

F #5422: Add save action to vm datatables (#1581)

(cherry picked from commit 71b92d87241e6fe98e3dbfc1f4e73c2223e9aa09)
This commit is contained in:
Sergio Betanzos 2021-11-18 17:40:35 +01:00 committed by Tino Vazquez
parent cfdcc107e2
commit 5031943f81
No known key found for this signature in database
GPG Key ID: 14201E424D02047E
10 changed files with 132 additions and 11 deletions

View File

@ -0,0 +1,21 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
* *
* Licensed 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. *
* ------------------------------------------------------------------------- */
import { createForm } from 'client/utils'
import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/SaveAsTemplateForm/schema'
const SaveAsTemplateForm = createForm(SCHEMA, FIELDS)
export default SaveAsTemplateForm

View File

@ -0,0 +1,45 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
* *
* Licensed 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. *
* ------------------------------------------------------------------------- */
import { string, boolean, object, ObjectSchema } from 'yup'
import { T, INPUT_TYPES } from 'client/constants'
import { Field, getValidationFromFields } from 'client/utils'
/** @type {Field} Template name field */
const NAME = {
name: 'name',
label: T.TemplateName,
type: INPUT_TYPES.TEXT,
validation: string()
.trim()
.required()
.default(() => undefined)
}
/** @type {Field} Persistent field */
const PERSISTENT = {
name: 'persistent',
label: T.MakeNewImagePersistent,
type: INPUT_TYPES.CHECKBOX,
validation: boolean().default(() => false),
grid: { md: 12 }
}
/** @type {Field[]} List of fields */
export const FIELDS = [NAME, PERSISTENT]
/** @type {ObjectSchema} Schema */
export const SCHEMA = object(getValidationFromFields(FIELDS))

View File

@ -23,6 +23,7 @@ import RecoverForm from 'client/components/Forms/Vm/RecoverForm'
import ResizeCapacityForm from 'client/components/Forms/Vm/ResizeCapacityForm'
import ResizeDiskForm from 'client/components/Forms/Vm/ResizeDiskForm'
import SaveAsDiskForm from 'client/components/Forms/Vm/SaveAsDiskForm'
import SaveAsTemplateForm from 'client/components/Forms/Vm/SaveAsTemplateForm'
export * from 'client/components/Forms/Vm/AttachDiskForm'
export * from 'client/components/Forms/Vm/CreateSchedActionForm'
@ -36,5 +37,6 @@ export {
RecoverForm,
ResizeCapacityForm,
ResizeDiskForm,
SaveAsDiskForm
SaveAsDiskForm,
SaveAsTemplateForm
}

View File

@ -245,7 +245,7 @@ const Actions = () => {
onSubmit: async (_, rows) => {
const ids = rows?.map?.(({ original }) => original?.ID)
await Promise.all(ids.map(id => remove(id)))
await Promise.all(ids.map(id => getVmTemplate(id)))
await getVmTemplates()
}
}]
}

View File

@ -31,10 +31,18 @@ import {
} from 'iconoir-react'
import { useAuth } from 'client/features/Auth'
import { useGeneralApi } from 'client/features/General'
import { useDatastore, useVmApi } from 'client/features/One'
import { Translate } from 'client/components/HOC'
import { RecoverForm, ChangeUserForm, ChangeGroupForm, MigrateForm } from 'client/components/Forms/Vm'
import {
RecoverForm,
ChangeUserForm,
ChangeGroupForm,
MigrateForm,
SaveAsTemplateForm
} from 'client/components/Forms/Vm'
import { createActions } from 'client/components/Tables/Enhanced/Utils'
import { PATH } from 'client/apps/sunstone/routesOne'
import { getLastHistory, isAvailableAction } from 'client/models/VirtualMachine'
@ -52,7 +60,12 @@ const ListVmNames = ({ rows = [] }) => {
const DS_NAME = datastores?.find(ds => ds?.ID === DS_ID)?.NAME ?? '--'
return (
<Typography key={`vm-${id}`} variant='inherit'>
<Typography
key={`vm-${id}`}
variant='inherit'
component='span'
display='block'
>
<Translate
word={T.WhereIsRunning}
values={[
@ -78,9 +91,11 @@ const MessageToConfirmAction = rows => (
const Actions = () => {
const history = useHistory()
const { view, getResourceView } = useAuth()
const { enqueueSuccess } = useGeneralApi()
const {
getVm,
getVms,
saveAsTemplate,
terminate,
terminateHard,
undeploy,
@ -144,7 +159,18 @@ const Actions = () => {
tooltip: T.SaveAsTemplate,
selected: { max: 1 },
icon: SaveFloppyDisk,
action: () => {}
options: [{
dialogProps: {
title: T.SaveAsTemplate,
subheader: SubHeader
},
form: SaveAsTemplateForm,
onSubmit: async (formData, rows) => {
const vmId = rows?.[0]?.original?.ID
const response = await saveAsTemplate(vmId, formData)
enqueueSuccess(response)
}
}]
},
{
tooltip: T.Manage,

View File

@ -122,7 +122,7 @@ const Cancel = props => <ActionButton action='cancel' icon={CancelIcon} {...prop
Copy.displayName = 'CopyActionButton'
Copy.propTypes = {
...ActionButton,
name: PropTypes.string.isRequired,
value: PropTypes.string
}

View File

@ -358,6 +358,8 @@ module.exports = {
/* VM schema - general */
Logo: 'Logo',
Hypervisor: 'Hypervisor',
TemplateName: 'Template name',
MakeNewImagePersistent: 'Make the new images persistent',
/* VM schema - ownership */
InstantiateAsUser: 'Instantiate as different User',
InstantiateAsGroup: 'Instantiate as different Group',
@ -400,11 +402,11 @@ module.exports = {
vCenterInstanceId: 'vCenter instance ID',
vCenterVmFolder: 'vCenter VM folder',
vCenterVmFolderConcept: `
If specified, the the VMs and Template folder path where
the VM will be created inside the data center.
The path is delimited by slashes (e.g /Management/VMs).
If no path is set the VM will be placed in the same folder where
the template is located.
If specified, the VMs and Template folder path where
the VM will be created inside the data center.
The path is delimited by slashes (e.g /Management/VMs).
If no path is set the VM will be placed in the same folder where
the template is located.
`,
/* VM Template schema - placement */
HostReqExpression: 'Host requirements expression',

View File

@ -72,6 +72,7 @@ export const changeOwnership = createAction(`${VM}/chown`, vmService.changeOwner
export const attachDisk = createAction(`${VM}/attach/disk`, vmService.attachDisk)
export const detachDisk = createAction(`${VM}/detach/disk`, vmService.detachDisk)
export const saveAsDisk = createAction(`${VM}/saveas/disk`, vmService.saveAsDisk)
export const saveAsTemplate = createAction(`${VM}/saveas/template`, vmService.saveAsTemplate)
export const resizeDisk = createAction(`${VM}/resize/disk`, vmService.resizeDisk)
export const createDiskSnapshot = createAction(`${VM}/create/disk-snapshot`, vmService.createDiskSnapshot)
export const renameDiskSnapshot = createAction(`${VM}/rename/disk-snapshot`, vmService.renameDiskSnapshot)

View File

@ -62,6 +62,7 @@ export const useVmApi = () => {
attachDisk: (id, template) => unwrapDispatch(actions.attachDisk({ id, template })),
detachDisk: (id, disk) => unwrapDispatch(actions.detachDisk({ id, disk })),
saveAsDisk: (id, data) => unwrapDispatch(actions.saveAsDisk({ id, ...data })),
saveAsTemplate: (id, data) => unwrapDispatch(actions.saveAsTemplate({ id, ...data })),
resizeDisk: (id, data) => unwrapDispatch(actions.resizeDisk({ id, ...data })),
createDiskSnapshot: (id, data) =>
unwrapDispatch(actions.createDiskSnapshot({ id, ...data })),

View File

@ -99,6 +99,29 @@ export const vmService = ({
return res?.data?.VM ?? {}
},
/**
* Clones the VM's source Template, replacing the disks with live snapshots
* of the current disks. The VM capacity and NICs are also preserved.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.name - Template name
* @param {boolean} params.persistent - Make the new images persistent
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
*/
saveAsTemplate: async ({ id, name, persistent }) => {
const res = await RestClient.request({
url: `/api/vm/save/${id}`,
method: 'POST',
data: { name, persistent }
})
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
return res?.data
},
/**
* Renames a virtual machine.
*