mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-09 22:14:13 +09:00
F OpennNebula/one#6880: Update backup configuration on a virtual machine (#3540)
Signed-off-by: Ángel Cívico Martos <acivico@opennnebula.io>
This commit is contained in:
1 parent
03fb9cc10c
commit
33db8abce7
10 files changed
+162
-85
No files matched your search
+7
-15
@@ -13,27 +13,21 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import { Stack } from '@mui/material'
|
||||
|
||||
import { FormWithSchema } from 'client/components/Forms'
|
||||
|
||||
import { SECTIONS } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/backup/schema'
|
||||
import { T } from 'client/constants'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
/**
|
||||
* @param {object} props - Component props
|
||||
* @param {object} props - Component properties
|
||||
* @param {object} props.oneConfig - OpenNEbula configuration
|
||||
* @param {boolean} props.adminGroup - If the user is admin
|
||||
* @returns {ReactElement} IO section component
|
||||
* @returns {ReactElement} Form content component
|
||||
*/
|
||||
const Backup = ({ oneConfig, adminGroup }) => (
|
||||
<Stack
|
||||
display="grid"
|
||||
gap="1em"
|
||||
sx={{ gridTemplateColumns: { sm: '1fr', md: '1fr 1fr' } }}
|
||||
>
|
||||
const Content = ({ oneConfig, adminGroup }) => (
|
||||
<Stack display="grid" gap="1em" sx={{ gridTemplateColumns: '1fr' }}>
|
||||
{SECTIONS(oneConfig, adminGroup).map(({ id, ...section }) => (
|
||||
<FormWithSchema
|
||||
key={id}
|
||||
@@ -45,11 +39,9 @@ const Backup = ({ oneConfig, adminGroup }) => (
|
||||
</Stack>
|
||||
)
|
||||
|
||||
Backup.displayName = 'Backup'
|
||||
|
||||
Backup.propTypes = {
|
||||
Content.propTypes = {
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default Backup
|
||||
export default Content
|
||||
@@ -0,0 +1,52 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2024, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2025, 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 ContentForm from 'client/components/Forms/Vm/BackupConfigForm/content'
|
||||
import { SCHEMA } from 'client/components/Forms/Vm/BackupConfigForm/schema'
|
||||
import { createForm } from 'client/utils'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
/**
|
||||
* @param {object} props - Component props
|
||||
* @param {object} props.oneConfig - OpenNEbula configuration
|
||||
* @param {boolean} props.adminGroup - If the user is admin
|
||||
* @returns {ReactElement} IO section component
|
||||
*/
|
||||
const BackupConfigForm = createForm(SCHEMA, undefined, { ContentForm })
|
||||
|
||||
BackupConfigForm.displayName = 'BackupConfigForm'
|
||||
|
||||
BackupConfigForm.propTypes = {
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default BackupConfigForm
|
||||
File renamed without changes.
@@ -13,24 +13,22 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import {
|
||||
SystemShut as OsIcon,
|
||||
DataTransferBoth as IOIcon,
|
||||
Folder as ContextIcon,
|
||||
RefreshDouble as BackupIcon,
|
||||
DataTransferBoth as IOIcon,
|
||||
SystemShut as OsIcon,
|
||||
} from 'iconoir-react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import InputOutput from 'client/components/Forms/Vm/UpdateConfigurationForm/inputOutput'
|
||||
import Booting from 'client/components/Forms/Vm/UpdateConfigurationForm/booting'
|
||||
import Context from 'client/components/Forms/Vm/UpdateConfigurationForm/context'
|
||||
import Backup from 'client/components/Forms/Vm/UpdateConfigurationForm/backup'
|
||||
import InputOutput from 'client/components/Forms/Vm/UpdateConfigurationForm/inputOutput'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T, HYPERVISORS } from 'client/constants'
|
||||
import Tabs from 'client/components/Tabs'
|
||||
import { HYPERVISORS, T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {object} props - Component props
|
||||
@@ -87,17 +85,6 @@ const Content = ({ hypervisor, oneConfig, adminGroup, vm }) => {
|
||||
),
|
||||
error: !!errors?.CONTEXT,
|
||||
},
|
||||
{
|
||||
id: 'backup',
|
||||
icon: BackupIcon,
|
||||
label: <Translate word={T.Backup} />,
|
||||
renderContent: () => (
|
||||
<Backup oneConfig={oneConfig} adminGroup={adminGroup} />
|
||||
),
|
||||
error: ['BACKUP_VOLATILE', 'FS_FREEZE', 'KEEP_LAST', 'MODE'].some(
|
||||
(id) => errors?.[`BACKUP_CONFIG.${id}`]
|
||||
),
|
||||
},
|
||||
],
|
||||
[errors, hypervisor]
|
||||
)
|
||||
|
||||
@@ -15,22 +15,21 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { reach } from 'yup'
|
||||
|
||||
import { set } from 'lodash'
|
||||
import { SCHEMA } from 'client/components/Forms/Vm/UpdateConfigurationForm/schema'
|
||||
import ContentForm from 'client/components/Forms/Vm/UpdateConfigurationForm/content'
|
||||
import { SCHEMA } from 'client/components/Forms/Vm/UpdateConfigurationForm/schema'
|
||||
import {
|
||||
createForm,
|
||||
decodeBase64,
|
||||
getUnknownAttributes,
|
||||
isBase64,
|
||||
decodeBase64,
|
||||
} from 'client/utils'
|
||||
import { set } from 'lodash'
|
||||
|
||||
const UpdateConfigurationForm = createForm(SCHEMA, undefined, {
|
||||
ContentForm,
|
||||
transformInitialValue: (vmTemplate, schema) => {
|
||||
const template = vmTemplate?.TEMPLATE ?? {}
|
||||
const context = template?.CONTEXT ?? {}
|
||||
const backupConfig = vmTemplate?.BACKUPS?.BACKUP_CONFIG ?? {}
|
||||
const bootOrder = template?.OS?.BOOT
|
||||
const nics = [].concat(template?.NIC ?? []).flat()
|
||||
const knownTemplate = schema.cast(
|
||||
@@ -44,15 +43,6 @@ const UpdateConfigurationForm = createForm(SCHEMA, undefined, {
|
||||
context: { ...template },
|
||||
})
|
||||
|
||||
// Get the custom vars from the context
|
||||
const knownBackupConfig = reach(schema, 'BACKUP_CONFIG').cast(
|
||||
backupConfig,
|
||||
{
|
||||
stripUnknown: true,
|
||||
context: { ...template },
|
||||
}
|
||||
)
|
||||
|
||||
// Merge known and unknown context custom vars
|
||||
knownTemplate.CONTEXT = {
|
||||
...knownContext,
|
||||
@@ -68,12 +58,6 @@ const UpdateConfigurationForm = createForm(SCHEMA, undefined, {
|
||||
}
|
||||
}
|
||||
|
||||
// Merge known and unknown context custom vars
|
||||
knownTemplate.BACKUP_CONFIG = {
|
||||
...knownBackupConfig,
|
||||
...getUnknownAttributes(backupConfig, knownBackupConfig),
|
||||
}
|
||||
|
||||
// Easy compatibility with the bootOrder component by specifying the same form paths as in the VM Template
|
||||
!!bootOrder && set(knownTemplate, 'extra.OS.BOOT', bootOrder)
|
||||
!!template?.DISK &&
|
||||
|
||||
@@ -17,9 +17,8 @@ import { object, ObjectSchema } from 'yup'
|
||||
|
||||
import { HYPERVISORS } from 'client/constants'
|
||||
import { SCHEMA as OS_SCHEMA } from './booting/schema'
|
||||
import { SCHEMA as IO_SCHEMA } from './inputOutput/schema'
|
||||
import { SCHEMA as CONTEXT_SCHEMA } from './context/schema'
|
||||
import { SCHEMA as BACKUP_SCHEMA } from './backup/schema'
|
||||
import { SCHEMA as IO_SCHEMA } from './inputOutput/schema'
|
||||
|
||||
/**
|
||||
* @param {object} [formProps] - Form props
|
||||
@@ -31,6 +30,5 @@ export const SCHEMA = ({ hypervisor }) =>
|
||||
.concat(IO_SCHEMA({ hypervisor }))
|
||||
.concat(OS_SCHEMA({ hypervisor }))
|
||||
.concat(CONTEXT_SCHEMA({ hypervisor }))
|
||||
.concat(BACKUP_SCHEMA())
|
||||
|
||||
export { IO_SCHEMA, OS_SCHEMA, CONTEXT_SCHEMA, BACKUP_SCHEMA }
|
||||
export { CONTEXT_SCHEMA, IO_SCHEMA, OS_SCHEMA }
|
||||
@@ -194,6 +194,13 @@ const UpdateConfigurationForm = (configProps) =>
|
||||
const BackupForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'Vm/BackupForm' }, configProps)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
*/
|
||||
const BackupConfigForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'Vm/BackupConfigForm' }, configProps)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
@@ -202,9 +209,12 @@ const AttachPciForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'Vm/AttachPciForm' }, configProps)
|
||||
|
||||
export {
|
||||
AliasForm,
|
||||
AttachAliasForm,
|
||||
AttachNicForm,
|
||||
AttachPciForm,
|
||||
AttachSecGroupForm,
|
||||
BackupConfigForm,
|
||||
BackupForm,
|
||||
ChangeGroupForm,
|
||||
ChangeUserForm,
|
||||
@@ -225,6 +235,4 @@ export {
|
||||
UpdateConfigurationForm,
|
||||
UpdateNicForm,
|
||||
VolatileSteps,
|
||||
AliasForm,
|
||||
AttachAliasForm,
|
||||
}
|
||||
@@ -13,23 +13,23 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
import { generatePath } from 'react-router-dom'
|
||||
|
||||
import {
|
||||
useRenameImageMutation,
|
||||
useChangeImageTypeMutation,
|
||||
usePersistentImageMutation,
|
||||
} from 'client/features/OneApi/image'
|
||||
import { StatusChip } from 'client/components/Status'
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
import {
|
||||
useChangeImageTypeMutation,
|
||||
usePersistentImageMutation,
|
||||
useRenameImageMutation,
|
||||
} from 'client/features/OneApi/image'
|
||||
|
||||
import { getDiskType, getType, getState } from 'client/models/Image'
|
||||
import { timeToString, booleanToString } from 'client/models/Helper'
|
||||
import { arrayToOptions, prettyBytes } from 'client/utils'
|
||||
import { T, Image, IMAGE_ACTIONS, IMAGE_TYPES } from 'client/constants'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { Image, IMAGE_ACTIONS, IMAGE_TYPES, T } from 'client/constants'
|
||||
import { booleanToString, timeToString } from 'client/models/Helper'
|
||||
import { getDiskType, getState, getType } from 'client/models/Image'
|
||||
import { arrayToOptions, prettyBytes } from 'client/utils'
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
|
||||
@@ -13,44 +13,99 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useGetVmQuery } from 'client/features/OneApi/vm'
|
||||
import { BackupsTable } from 'client/components/Tables'
|
||||
import { useHistory, generatePath } from 'react-router-dom'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import { BackupsTable } from 'client/components/Tables'
|
||||
import { T } from 'client/constants'
|
||||
import {
|
||||
useGetVmQuery,
|
||||
useUpdateConfigurationMutation,
|
||||
} from 'client/features/OneApi/vm'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
import { generatePath, useHistory } from 'react-router-dom'
|
||||
|
||||
import { jsonToXml } from 'client/models/Helper'
|
||||
|
||||
import { BackupConfigForm } from 'client/components/Forms/Vm'
|
||||
|
||||
import { Stack } from '@mui/material'
|
||||
|
||||
/**
|
||||
* Renders the list of backups from a VM.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.id - Virtual Machine id
|
||||
* @param {object} props.oneConfig - OpenNEbula configuration
|
||||
* @param {boolean} props.adminGroup - If the user is admin
|
||||
* @returns {ReactElement} Backups tab
|
||||
*/
|
||||
const VmBackupTab = ({ id }) => {
|
||||
const VmBackupTab = ({ id, oneConfig, adminGroup }) => {
|
||||
const { data: vm = {}, refetch, isFetching } = useGetVmQuery({ id })
|
||||
const path = PATH.STORAGE.BACKUPS.DETAIL
|
||||
const history = useHistory()
|
||||
|
||||
const [updateConf] = useUpdateConfigurationMutation()
|
||||
|
||||
const handleRowClick = (rowId) => {
|
||||
history.push(generatePath(path, { id: String(rowId) }))
|
||||
}
|
||||
|
||||
const handleUpdateBackupConf = async (newConfiguration) => {
|
||||
const xml = jsonToXml(newConfiguration)
|
||||
await updateConf({ id, template: xml })
|
||||
}
|
||||
|
||||
return (
|
||||
<BackupsTable
|
||||
disableRowSelect
|
||||
disableGlobalSort
|
||||
refetchVm={refetch}
|
||||
isFetchingVm={isFetching}
|
||||
vm={vm}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
/>
|
||||
<>
|
||||
{/* <Backup oneConfig={oneConfig} adminGroup={adminGroup} /> */}
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="start"
|
||||
gap="1rem"
|
||||
marginBottom="1rem"
|
||||
>
|
||||
<ButtonToTriggerForm
|
||||
buttonProps={{
|
||||
'data-cy': 'backup-vm',
|
||||
label: T.BackupConfigVM,
|
||||
disabled: isFetching,
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
dialogProps: {
|
||||
title: T.BackupConfigVM,
|
||||
dataCy: 'modal-backup-vm',
|
||||
},
|
||||
form: () =>
|
||||
BackupConfigForm({
|
||||
stepProps: { oneConfig, adminGroup, vm },
|
||||
initialValues: vm,
|
||||
}),
|
||||
onSubmit: handleUpdateBackupConf,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<BackupsTable
|
||||
disableRowSelect
|
||||
disableGlobalSort
|
||||
refetchVm={refetch}
|
||||
isFetchingVm={isFetching}
|
||||
vm={vm}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
VmBackupTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
VmBackupTab.displayName = 'VmBackupTab'
|
||||
|
||||
@@ -1233,6 +1233,7 @@ module.exports = {
|
||||
/* VM Template schema - Backup */
|
||||
BackupVolatileDisksQuestion: 'Backup volatile disks?',
|
||||
BackupVolatile: 'Backup volatile',
|
||||
BackupConfigVM: 'Backup config',
|
||||
FSFreeze: 'FS Freeze',
|
||||
Execution: 'Execution',
|
||||
HowManyBackupsQuestion: 'How many backups do you want to keep?',
|
||||
|
||||
Reference in new issue
Block a user