From 33db8abce7a3cb869e924032d3258ba0615273cb Mon Sep 17 00:00:00 2001 From: Angel Date: Thu, 3 Apr 2025 17:21:09 +0200 Subject: [PATCH] F OpennNebula/one#6880: Update backup configuration on a virtual machine (#3540) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ángel Cívico Martos --- .../index.js => BackupConfigForm/content.js} | 22 ++--- .../Forms/Vm/BackupConfigForm/index.js | 52 ++++++++++++ .../backup => BackupConfigForm}/schema.js | 0 .../Vm/UpdateConfigurationForm/content.js | 29 ++----- .../Forms/Vm/UpdateConfigurationForm/index.js | 22 +---- .../Vm/UpdateConfigurationForm/schema.js | 6 +- .../src/client/components/Forms/Vm/index.js | 12 ++- .../Tabs/Backup/Info/information.js | 20 ++--- .../src/client/components/Tabs/Vm/Backup.js | 83 +++++++++++++++---- .../src/client/constants/translates.js | 1 + 10 files changed, 162 insertions(+), 85 deletions(-) rename src/fireedge/src/client/components/Forms/Vm/{UpdateConfigurationForm/backup/index.js => BackupConfigForm/content.js} (85%) create mode 100644 src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/index.js rename src/fireedge/src/client/components/Forms/Vm/{UpdateConfigurationForm/backup => BackupConfigForm}/schema.js (100%) diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js b/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/content.js similarity index 85% rename from src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js rename to src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/content.js index ed942bcfd4..d8c551ef8a 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/content.js @@ -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 }) => ( - +const Content = ({ oneConfig, adminGroup }) => ( + {SECTIONS(oneConfig, adminGroup).map(({ id, ...section }) => ( ( ) -Backup.displayName = 'Backup' - -Backup.propTypes = { +Content.propTypes = { oneConfig: PropTypes.object, adminGroup: PropTypes.bool, } -export default Backup +export default Content diff --git a/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/index.js b/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/index.js new file mode 100644 index 0000000000..66c0f646b1 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/index.js @@ -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 diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/schema.js b/src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/schema.js similarity index 100% rename from src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/schema.js rename to src/fireedge/src/client/components/Forms/Vm/BackupConfigForm/schema.js diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js index 9b3926c6e1..b1331d68d2 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js @@ -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: , - renderContent: () => ( - - ), - error: ['BACKUP_VOLATILE', 'FS_FREEZE', 'KEEP_LAST', 'MODE'].some( - (id) => errors?.[`BACKUP_CONFIG.${id}`] - ), - }, ], [errors, hypervisor] ) diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js index 217c97a5d9..2c7ae028be 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/index.js @@ -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 && diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/schema.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/schema.js index b97789928a..1cb1495191 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/schema.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/schema.js @@ -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 } diff --git a/src/fireedge/src/client/components/Forms/Vm/index.js b/src/fireedge/src/client/components/Forms/Vm/index.js index bbe22780b8..660cffb1dc 100644 --- a/src/fireedge/src/client/components/Forms/Vm/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/index.js @@ -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, } diff --git a/src/fireedge/src/client/components/Tabs/Backup/Info/information.js b/src/fireedge/src/client/components/Tabs/Backup/Info/information.js index ce32ad0403..a541efa002 100644 --- a/src/fireedge/src/client/components/Tabs/Backup/Info/information.js +++ b/src/fireedge/src/client/components/Tabs/Backup/Info/information.js @@ -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. diff --git a/src/fireedge/src/client/components/Tabs/Vm/Backup.js b/src/fireedge/src/client/components/Tabs/Vm/Backup.js index dd829d49a1..72087502b5 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Backup.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Backup.js @@ -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 ( - handleRowClick(row.ID)} - /> + <> + {/* */} + + + BackupConfigForm({ + stepProps: { oneConfig, adminGroup, vm }, + initialValues: vm, + }), + onSubmit: handleUpdateBackupConf, + }, + ]} + /> + + + handleRowClick(row.ID)} + /> + ) } VmBackupTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } VmBackupTab.displayName = 'VmBackupTab' diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index dd10a7cff1..cd3beb6264 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -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?',