diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js index 17b6d46ca3..8bc0136e8d 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/backup/index.js @@ -20,17 +20,21 @@ 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' /** + * @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 Backup = () => ( +const Backup = ({ oneConfig, adminGroup }) => ( - {SECTIONS.map(({ id, ...section }) => ( + {SECTIONS(oneConfig, adminGroup).map(({ id, ...section }) => ( ( Backup.displayName = 'Backup' +Backup.propTypes = { + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} + export default Backup 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 d16c5fe943..72c925c059 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/content.js @@ -35,9 +35,11 @@ import { T, HYPERVISORS } from 'client/constants' /** * @param {object} props - Component props * @param {HYPERVISORS} props.hypervisor - VM hypervisor + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Form content component */ -const Content = ({ hypervisor }) => { +const Content = ({ hypervisor, oneConfig, adminGroup }) => { const { formState: { errors }, } = useFormContext() @@ -48,28 +50,48 @@ const Content = ({ hypervisor }) => { id: 'booting', icon: OsIcon, label: , - renderContent: () => , + renderContent: () => ( + + ), error: !!errors?.OS, }, { id: 'input_output', icon: IOIcon, label: , - renderContent: () => , + renderContent: () => ( + + ), error: ['GRAPHICS', 'INPUT'].some((id) => errors?.[id]), }, { id: 'context', icon: ContextIcon, label: , - renderContent: () => , + renderContent: () => ( + + ), error: !!errors?.CONTEXT, }, { id: 'backup_config', icon: BackupIcon, label: , - renderContent: () => , + renderContent: () => ( + + ), error: ['BACKUP_VOLATILE', 'FS_FREEZE', 'KEEP_LAST', 'MODE'].some( (id) => errors?.[`BACKUP_CONFIG.${id}`] ), @@ -78,9 +100,13 @@ const Content = ({ hypervisor }) => { [errors, hypervisor] ) - return + return } -Content.propTypes = { hypervisor: PropTypes.string } +Content.propTypes = { + hypervisor: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} export default Content diff --git a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/context/index.js b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/context/index.js index a7bc38d2ea..c377867705 100644 --- a/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/context/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/UpdateConfigurationForm/context/index.js @@ -25,16 +25,34 @@ import { HYPERVISORS } from 'client/constants' /** * @param {object} props - Component props * @param {HYPERVISORS} props.hypervisor - VM hypervisor + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Context section component */ -const ContextSection = ({ hypervisor }) => ( +const ContextSection = ({ hypervisor, oneConfig, adminGroup }) => ( <> - - - + + + ) -ContextSection.propTypes = { hypervisor: PropTypes.string } +ContextSection.propTypes = { + hypervisor: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, +} export default ContextSection diff --git a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js index 3e518c7c6b..7b7bdce0df 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js @@ -38,9 +38,16 @@ const { UPDATE_CONF } = VM_ACTIONS * @param {object|boolean} props.tabProps - Tab properties * @param {object} [props.tabProps.actions] - Actions from tab view yaml * @param {string} props.id - Virtual machine id + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Configuration tab */ -const VmConfigurationTab = ({ tabProps: { actions } = {}, id }) => { +const VmConfigurationTab = ({ + tabProps: { actions } = {}, + id, + oneConfig, + adminGroup, +}) => { const [updateConf] = useUpdateConfigurationMutation() const { data: vm = {}, isFetching } = useGetVmQuery({ id }) const { TEMPLATE, BACKUPS } = vm @@ -128,7 +135,7 @@ const VmConfigurationTab = ({ tabProps: { actions } = {}, id }) => { }, form: () => UpdateConfigurationForm({ - stepProps: { hypervisor }, + stepProps: { hypervisor, oneConfig, adminGroup }, initialValues: vm, }), onSubmit: handleUpdateConf, @@ -181,6 +188,8 @@ const VmConfigurationTab = ({ tabProps: { actions } = {}, id }) => { VmConfigurationTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } VmConfigurationTab.displayName = 'VmConfigurationTab' diff --git a/src/fireedge/src/client/components/Tabs/Vm/Info/capacity.js b/src/fireedge/src/client/components/Tabs/Vm/Info/capacity.js index 0d1551ce20..94417debf9 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Info/capacity.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Info/capacity.js @@ -36,9 +36,11 @@ import { T, VM, VM_ACTIONS } from 'client/constants' * @param {object} props - Props * @param {VM} props.vm - Virtual machine * @param {string[]} props.actions - Available actions to capacity tab + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Capacity tab */ -const CapacityPanel = ({ vm = {}, actions }) => { +const CapacityPanel = ({ vm = {}, actions, oneConfig, adminGroup }) => { const { CPU, VCPU = '-', @@ -101,7 +103,19 @@ const CapacityPanel = ({ vm = {}, actions }) => { }, ].filter(Boolean) - return } list={info} /> + return ( + + } + list={info} + /> + ) } CapacityPanel.propTypes = { @@ -117,13 +131,24 @@ CapacityPanel.displayName = 'CapacityPanel' * @param {object} props - Props * @param {VM} props.vm - Virtual machine * @param {string[]} props.actions - Available actions to capacity tab + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Capacity panel header */ -const PanelHeader = ({ vm = {}, actions = [] }) => { +const PanelHeader = ({ vm = {}, actions = [], oneConfig, adminGroup }) => { const [resizeCapacity] = useResizeMutation() const handleResizeCapacity = async (formData) => { const { enforce, ...restOfData } = formData + + // #6154: If a restricted attribute is send to the core in resize operation, it will fail. So delete every restricted attribute for resize operation. + const restrictedAttributes = oneConfig?.VM_RESTRICTED_ATTR + Object.keys(restOfData).forEach((key) => { + if (restrictedAttributes.find((attribute) => attribute === key)) { + delete restOfData[key] + } + }) + const template = jsonToXml(restOfData) await resizeCapacity({ id: vm.ID, enforce, template }) @@ -160,7 +185,11 @@ const PanelHeader = ({ vm = {}, actions = [] }) => { title: T.ResizeCapacity, dataCy: 'modal-resize-capacity', }, - form: () => ResizeCapacityForm({ initialValues: vm.TEMPLATE }), + form: () => + ResizeCapacityForm({ + initialValues: vm.TEMPLATE, + stepProps: { oneConfig, adminGroup, nameParentAttribute: '' }, + }), onSubmit: handleResizeCapacity, }, ]} diff --git a/src/fireedge/src/client/components/Tabs/Vm/Info/index.js b/src/fireedge/src/client/components/Tabs/Vm/Info/index.js index 05d9724f88..cf4c47b0e4 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Info/index.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Info/index.js @@ -54,9 +54,11 @@ const HIDDEN_MONITORING_REG = * @param {object} props - Props * @param {object} props.tabProps - Tab information * @param {string} props.id - Virtual machine id + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Information tab */ -const VmInfoTab = ({ tabProps = {}, id }) => { +const VmInfoTab = ({ tabProps = {}, id, oneConfig, adminGroup }) => { const { information_panel: informationPanel, capacity_panel: capacityPanel, @@ -157,7 +159,12 @@ const VmInfoTab = ({ tabProps = {}, id }) => { )} {capacityPanel?.enabled && ( <> - + )} @@ -203,6 +210,8 @@ const VmInfoTab = ({ tabProps = {}, id }) => { VmInfoTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } VmInfoTab.displayName = 'VmInfoTab' diff --git a/src/fireedge/src/client/components/Tabs/Vm/Network/index.js b/src/fireedge/src/client/components/Tabs/Vm/Network/index.js index d18b24d0fc..4b5e25bf8d 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Network/index.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Network/index.js @@ -50,9 +50,16 @@ const { * @param {object} props.tabProps - Tab information * @param {string[]} props.tabProps.actions - Actions tab * @param {string} props.id - Virtual Machine id + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Networks tab */ -const VmNetworkTab = ({ tabProps: { actions } = {}, id }) => { +const VmNetworkTab = ({ + tabProps: { actions } = {}, + id, + oneConfig, + adminGroup, +}) => { const { data: vm } = useGetVmQuery({ id }) const [nics, hypervisor, actionsAvailable] = useMemo(() => { @@ -72,7 +79,13 @@ const VmNetworkTab = ({ tabProps: { actions } = {}, id }) => { return (
{actionsAvailable?.includes?.(ATTACH_NIC) && ( - + )} @@ -123,6 +136,8 @@ const VmNetworkTab = ({ tabProps: { actions } = {}, id }) => { VmNetworkTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } VmNetworkTab.displayName = 'VmNetworkTab' diff --git a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js index 6e50422cce..5a3dfcf1c6 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js @@ -58,9 +58,16 @@ const { * @param {object} props.tabProps - Tab information * @param {string[]} props.tabProps.actions - Actions tab * @param {string} props.id - Virtual Machine id + * @param {object} props.oneConfig - OpenNEbula configuration + * @param {boolean} props.adminGroup - If the user is admin * @returns {ReactElement} Storage tab */ -const VmStorageTab = ({ tabProps: { actions } = {}, id }) => { +const VmStorageTab = ({ + tabProps: { actions } = {}, + id, + oneConfig, + adminGroup, +}) => { const { data: vm = {} } = useGetVmQuery({ id }) const [disks, hypervisor, actionsAvailable] = useMemo(() => { @@ -76,7 +83,12 @@ const VmStorageTab = ({ tabProps: { actions } = {}, id }) => { return (
{actionsAvailable?.includes?.(ATTACH_DISK) && ( - + )} @@ -143,6 +155,8 @@ const VmStorageTab = ({ tabProps: { actions } = {}, id }) => { VmStorageTab.propTypes = { tabProps: PropTypes.object, id: PropTypes.string, + oneConfig: PropTypes.object, + adminGroup: PropTypes.bool, } VmStorageTab.displayName = 'VmStorageTab' diff --git a/src/fireedge/src/client/components/Tabs/Vm/index.js b/src/fireedge/src/client/components/Tabs/Vm/index.js index 19c279d2a5..5487dfdd21 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/index.js +++ b/src/fireedge/src/client/components/Tabs/Vm/index.js @@ -21,7 +21,7 @@ import { memo, useMemo } from 'react' import { Translate } from 'client/components/HOC' import { RESOURCE_NAMES, T } from 'client/constants' -import { useViews } from 'client/features/Auth' +import { useViews, useSystemData } from 'client/features/Auth' import { useGetVmQuery, useUpdateUserTemplateMutation, @@ -79,6 +79,8 @@ const VmTabs = memo(({ id }) => { const { USER_TEMPLATE, ID } = vm + const { adminGroup, oneConfig } = useSystemData() + const handleDismissError = async () => { const { ERROR, SCHED_MESSAGE, ...templateWithoutError } = USER_TEMPLATE const xml = jsonToXml({ ...templateWithoutError }) @@ -92,7 +94,13 @@ const VmTabs = memo(({ id }) => { const resource = RESOURCE_NAMES.VM const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {} - return getAvailableInfoTabs(infoTabs, getTabComponent, id) + return getAvailableInfoTabs( + infoTabs, + getTabComponent, + id, + oneConfig, + adminGroup + ) }, [view, id]) if (isError) { diff --git a/src/fireedge/src/client/models/Helper.js b/src/fireedge/src/client/models/Helper.js index 9aba15db5b..17b3d77027 100644 --- a/src/fireedge/src/client/models/Helper.js +++ b/src/fireedge/src/client/models/Helper.js @@ -308,13 +308,21 @@ export const getActionsAvailable = (actions = {}, hypervisor = '') => * @param {object} infoTabs - Info tabs from view yaml * @param {Function} getTabComponent - Function to get tab component * @param {string} id - Resource id + * @param {object} oneConfig - OpenNEbula configuration + * @param {boolean} adminGroup - If the user is admin * @returns {{ * id: string, * name: string, * renderContent: Function * }[]} - List of available info tabs for the resource */ -export const getAvailableInfoTabs = (infoTabs = {}, getTabComponent, id) => +export const getAvailableInfoTabs = ( + infoTabs = {}, + getTabComponent, + id, + oneConfig, + adminGroup +) => Object.entries(infoTabs) ?.filter(([_, { enabled } = {}]) => !!enabled) ?.map(([tabName, tabProps]) => { @@ -324,7 +332,14 @@ export const getAvailableInfoTabs = (infoTabs = {}, getTabComponent, id) => TabContent && { label: TabContent?.label ?? sentenceCase(tabName), id: tabName, - renderContent: () => , + renderContent: () => ( + + ), } ) }) diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js index 515844a453..d7511d566b 100644 --- a/src/fireedge/src/client/utils/schema.js +++ b/src/fireedge/src/client/utils/schema.js @@ -494,7 +494,28 @@ export const createForm = (schema, fields, extraParams = {}) => (props = {}, initialValues) => { const schemaCallback = typeof schema === 'function' ? schema(props) : schema - const fieldsCallback = typeof fields === 'function' ? fields(props) : fields + + const disable = + props?.oneConfig && + props?.adminGroup === false && + typeof props?.nameParentAttribute === 'string' + const fieldsCallback = disable + ? typeof fields === 'function' + ? disableFields( + fields(props), + props.nameParentAttribute, + props.oneConfig, + props.adminGroup + ) + : disableFields( + fields, + props.nameParentAttribute, + props.oneConfig, + props.adminGroup + ) + : typeof fields === 'function' + ? fields(props) + : fields const defaultTransformInitialValue = (values) => schemaCallback.cast(values, { stripUnknown: true })