mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-23 22:50:09 +03:00
B OpenNebula/one#6154: Fix restricted attributes in VM dialogs (#2755)
This commit is contained in:
parent
a8b1e4ca5a
commit
7b6cbf0f1d
@ -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 }) => (
|
||||
<Stack
|
||||
display="grid"
|
||||
gap="1em"
|
||||
sx={{ gridTemplateColumns: { sm: '1fr', md: '1fr 1fr' } }}
|
||||
>
|
||||
{SECTIONS.map(({ id, ...section }) => (
|
||||
{SECTIONS(oneConfig, adminGroup).map(({ id, ...section }) => (
|
||||
<FormWithSchema
|
||||
key={id}
|
||||
cy="backups-conf"
|
||||
@ -43,4 +47,9 @@ const Backup = () => (
|
||||
|
||||
Backup.displayName = 'Backup'
|
||||
|
||||
Backup.propTypes = {
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default Backup
|
||||
|
@ -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: <Translate word={T.OSAndCpu} />,
|
||||
renderContent: () => <Booting hypervisor={hypervisor} />,
|
||||
renderContent: () => (
|
||||
<Booting
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
),
|
||||
error: !!errors?.OS,
|
||||
},
|
||||
{
|
||||
id: 'input_output',
|
||||
icon: IOIcon,
|
||||
label: <Translate word={T.InputOrOutput} />,
|
||||
renderContent: () => <InputOutput hypervisor={hypervisor} />,
|
||||
renderContent: () => (
|
||||
<InputOutput
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
),
|
||||
error: ['GRAPHICS', 'INPUT'].some((id) => errors?.[id]),
|
||||
},
|
||||
{
|
||||
id: 'context',
|
||||
icon: ContextIcon,
|
||||
label: <Translate word={T.Context} />,
|
||||
renderContent: () => <Context hypervisor={hypervisor} />,
|
||||
renderContent: () => (
|
||||
<Context
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
),
|
||||
error: !!errors?.CONTEXT,
|
||||
},
|
||||
{
|
||||
id: 'backup_config',
|
||||
icon: BackupIcon,
|
||||
label: <Translate word={T.Backup} />,
|
||||
renderContent: () => <Backup />,
|
||||
renderContent: () => (
|
||||
<Backup oneConfig={oneConfig} adminGroup={adminGroup} />
|
||||
),
|
||||
error: ['BACKUP_VOLATILE', 'FS_FREEZE', 'KEEP_LAST', 'MODE'].some(
|
||||
(id) => errors?.[`BACKUP_CONFIG.${id}`]
|
||||
),
|
||||
@ -78,9 +100,13 @@ const Content = ({ hypervisor }) => {
|
||||
[errors, hypervisor]
|
||||
)
|
||||
|
||||
return <Tabs tabs={tabs} />
|
||||
return <Tabs tabs={tabs} oneConfig={oneConfig} adminGroup={adminGroup} />
|
||||
}
|
||||
|
||||
Content.propTypes = { hypervisor: PropTypes.string }
|
||||
Content.propTypes = {
|
||||
hypervisor: PropTypes.string,
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default Content
|
||||
|
@ -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 }) => (
|
||||
<>
|
||||
<ConfigurationSection hypervisor={hypervisor} />
|
||||
<FilesSection hypervisor={hypervisor} />
|
||||
<ContextVarsSection hypervisor={hypervisor} />
|
||||
<ConfigurationSection
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
<FilesSection
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
<ContextVarsSection
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
ContextSection.propTypes = { hypervisor: PropTypes.string }
|
||||
ContextSection.propTypes = {
|
||||
hypervisor: PropTypes.string,
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default ContextSection
|
||||
|
@ -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'
|
||||
|
@ -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 title={<PanelHeader vm={vm} actions={actions} />} list={info} />
|
||||
return (
|
||||
<List
|
||||
title={
|
||||
<PanelHeader
|
||||
vm={vm}
|
||||
actions={actions}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
}
|
||||
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,
|
||||
},
|
||||
]}
|
||||
|
@ -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 && (
|
||||
<>
|
||||
<Capacity actions={getActions(capacityPanel?.actions)} vm={vm} />
|
||||
<Capacity
|
||||
actions={getActions(capacityPanel?.actions)}
|
||||
vm={vm}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
<Graphs id={id} />
|
||||
</>
|
||||
)}
|
||||
@ -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'
|
||||
|
@ -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 (
|
||||
<div>
|
||||
{actionsAvailable?.includes?.(ATTACH_NIC) && (
|
||||
<AttachAction vmId={id} currentNics={nics} hypervisor={hypervisor} />
|
||||
<AttachAction
|
||||
vmId={id}
|
||||
currentNics={nics}
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack gap="1em" py="0.8em">
|
||||
@ -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'
|
||||
|
@ -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 (
|
||||
<div>
|
||||
{actionsAvailable?.includes?.(ATTACH_DISK) && (
|
||||
<AttachAction vmId={id} hypervisor={hypervisor} />
|
||||
<AttachAction
|
||||
vmId={id}
|
||||
hypervisor={hypervisor}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack gap="1em" py="0.8em">
|
||||
@ -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'
|
||||
|
@ -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) {
|
||||
|
@ -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: () => <TabContent tabProps={tabProps} id={id} />,
|
||||
renderContent: () => (
|
||||
<TabContent
|
||||
tabProps={tabProps}
|
||||
id={id}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
),
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -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 })
|
||||
|
Loading…
x
Reference in New Issue
Block a user