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

F #5422: Move capacity section to info tab (#2079)

This commit is contained in:
Sergio Betanzos 2022-05-25 18:47:50 +02:00 committed by GitHub
parent f90b25aa68
commit b12b55945a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 182 additions and 270 deletions

View File

@ -87,6 +87,10 @@ info-tabs:
actions:
chown: true
chgrp: true
capacity_panel:
enabled: true
actions:
resize_capacity: true
vcenter_panel:
enabled: true
actions:
@ -113,11 +117,6 @@ info-tabs:
edit: true
delete: true
capacity:
enabled: true
actions:
resize_capacity: true
storage:
enabled: true
actions:

View File

@ -88,6 +88,10 @@ info-tabs:
actions:
chown: false
chgrp: false
capacity_panel:
enabled: true
actions:
resize_capacity: true
vcenter_panel:
enabled: true
actions:
@ -114,11 +118,6 @@ info-tabs:
edit: false
delete: false
capacity:
enabled: true
actions:
resize_capacity: true
storage:
enabled: true
actions:

View File

@ -1,71 +0,0 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2022, 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 { ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import { useGetVmQuery, useResizeMutation } from 'client/features/OneApi/vm'
import InformationPanel from 'client/components/Tabs/Vm/Capacity/information'
import { getHypervisor, isAvailableAction } from 'client/models/VirtualMachine'
import { getActionsAvailable, jsonToXml } from 'client/models/Helper'
/**
* Renders capacity tab.
*
* @param {object} props - Props
* @param {object} props.tabProps - Tab information
* @param {string[]} props.tabProps.actions - Actions tab
* @param {string} props.id - Virtual Machine id
* @returns {ReactElement} Capacity tab
*/
const VmCapacityTab = ({ tabProps: { actions } = {}, id }) => {
const [resizeCapacity] = useResizeMutation()
const { data: vm = {} } = useGetVmQuery({ id })
const actionsAvailable = useMemo(() => {
const hypervisor = getHypervisor(vm)
const actionsByHypervisor = getActionsAvailable(actions, hypervisor)
const actionsByState = actionsByHypervisor.filter((action) =>
isAvailableAction(action, vm)
)
return actionsByState
}, [vm])
const handleResizeCapacity = async (formData) => {
const { enforce, ...restOfData } = formData
const template = jsonToXml(restOfData)
await resizeCapacity({ id: vm.ID, enforce, template })
}
return (
<InformationPanel
actions={actionsAvailable}
handleResizeCapacity={handleResizeCapacity}
vm={vm}
/>
)
}
VmCapacityTab.propTypes = {
tabProps: PropTypes.object,
id: PropTypes.string,
}
VmCapacityTab.displayName = 'VmCapacityTab'
export default VmCapacityTab

View File

@ -1,140 +0,0 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2022, 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 { useMemo, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
import { ResizeCapacityForm } from 'client/components/Forms/Vm'
import { Tr, Translate } from 'client/components/HOC'
import useCapacityTabStyles from 'client/components/Tabs/Vm/Capacity/styles'
import { isVCenter } from 'client/models/VirtualMachine'
import { formatNumberByCurrency } from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, VM_ACTIONS, VM } from 'client/constants'
/**
* Renders capacity information.
*
* @param {object} props - Props
* @param {string[]} props.actions - Actions tab
* @param {VM} props.vm - Virtual Machine id
* @param {string} props.handleResizeCapacity - Resize capacity
* @returns {ReactElement} Capacity information
*/
const InformationPanel = ({ actions, vm = {}, handleResizeCapacity }) => {
const classes = useCapacityTabStyles()
const { TEMPLATE } = vm
const memory = TEMPLATE?.MEMORY
const memoryCost = useMemo(() => {
const cost = TEMPLATE?.MEMORY_COST || 0
const monthCost = formatNumberByCurrency(memory * cost * 24 * 30)
return <Translate word={T.CostEachMonth} values={[monthCost]} />
}, [memory, TEMPLATE?.MEMORY_COST])
const cpu = TEMPLATE?.CPU
const cpuCost = useMemo(() => {
const cost = TEMPLATE?.CPU_COST || 0
const monthCost = formatNumberByCurrency(cpu * cost * 24 * 30)
return <Translate word={T.CostEachMonth} values={[monthCost]} />
}, [cpu, TEMPLATE?.CPU_COST])
const capacity = [
{
name: T.PhysicalCpu,
value: cpu,
dataCy: 'cpu',
},
{
name: T.VirtualCpu,
value: TEMPLATE?.VCPU ?? '-',
dataCy: 'virtualcpu',
},
isVCenter(vm) && {
name: T.VirtualCores,
value: (
<>
{`${Tr(T.Cores)} x ${TEMPLATE?.TOPOLOGY?.CORES || '-'} |
${Tr(T.Sockets)} ${TEMPLATE?.TOPOLOGY?.SOCKETS || '-'}`}
</>
),
dataCy: 'virtualcores',
},
{
name: T.Memory,
value: prettyBytes(+memory, 'MB'),
dataCy: 'memory',
},
{
name: T.CostCpu,
value: cpuCost,
dataCy: 'cpucost',
},
{
name: T.CostMemory,
value: memoryCost,
dataCy: 'memorycost',
},
].filter(Boolean)
return (
<div className={classes.root}>
<div className={classes.actions}>
{actions?.includes?.(VM_ACTIONS.RESIZE_CAPACITY) && (
<ButtonToTriggerForm
buttonProps={{
color: 'secondary',
'data-cy': 'resize-capacity',
label: T.Resize,
variant: 'outlined',
}}
options={[
{
dialogProps: { title: T.ResizeCapacity },
form: () => ResizeCapacityForm({ initialValues: vm.TEMPLATE }),
onSubmit: handleResizeCapacity,
},
]}
/>
)}
</div>
{capacity.map(({ name, value, dataCy }) => (
<div key={name} className={classes.item}>
<Typography fontWeight="medium" noWrap title={name}>
{name}
</Typography>
<Typography variant="body2" noWrap title={value} data-cy={dataCy}>
{value}
</Typography>
</div>
))}
</div>
)
}
InformationPanel.propTypes = {
handleResizeCapacity: PropTypes.func,
actions: PropTypes.array,
vm: PropTypes.object,
}
InformationPanel.displayName = 'InformationPanel'
export default InformationPanel

View File

@ -1,47 +0,0 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2022, 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 makeStyles from '@mui/styles/makeStyles'
export default makeStyles((theme) => ({
root: {
padding: '0.5em',
display: 'grid',
gap: '1em',
gridAutoFlow: 'column',
[theme.breakpoints.down('md')]: {
gridAutoFlow: 'initial',
},
},
item: {
[theme.breakpoints.down('md')]: {
display: 'flex',
gap: '1em',
'& > *': {
width: '50%',
},
},
},
actions: {
[theme.breakpoints.down('md')]: {
borderBottom: `1px solid ${theme.palette.divider}`,
padding: '0 1em 1em 1em',
},
[theme.breakpoints.up('md')]: {
order: 1,
textAlign: 'end',
},
},
}))

View File

@ -0,0 +1,169 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2022, 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 { ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Edit as EditIcon } from 'iconoir-react'
import { Stack, Typography } from '@mui/material'
import { useResizeMutation } from 'client/features/OneApi/vm'
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
import { ResizeCapacityForm } from 'client/components/Forms/Vm'
import { List } from 'client/components/Tabs/Common'
import { Tr, Translate } from 'client/components/HOC'
import { isVCenter, isAvailableAction } from 'client/models/VirtualMachine'
import { formatNumberByCurrency, jsonToXml } from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, VM, VM_ACTIONS } from 'client/constants'
/**
* Renders mainly capacity tab.
*
* @param {object} props - Props
* @param {VM} props.vm - Virtual machine
* @param {string[]} props.actions - Available actions to capacity tab
* @returns {ReactElement} Capacity tab
*/
const CapacityPanel = ({ vm = {}, actions }) => {
const {
CPU,
VCPU = '-',
MEMORY,
CPU_COST,
MEMORY_COST,
TOPOLOGY: { CORES = '-', SOCKETS = '-' } = {},
} = vm?.TEMPLATE || {}
const memoryCost = useMemo(() => {
const cost = MEMORY_COST || 0
const monthCost = formatNumberByCurrency(MEMORY * cost * 24 * 30)
return <Translate word={T.CostEachMonth} values={[monthCost]} />
}, [MEMORY, MEMORY_COST])
const cpuCost = useMemo(() => {
const cost = CPU_COST || 0
const monthCost = formatNumberByCurrency(CPU * cost * 24 * 30)
return <Translate word={T.CostEachMonth} values={[monthCost]} />
}, [CPU, CPU_COST])
const info = [
{
name: T.PhysicalCpu,
value: CPU,
dataCy: 'cpu',
},
{
name: T.VirtualCpu,
value: VCPU,
dataCy: 'vcpu',
},
isVCenter(vm) && {
name: T.VirtualCores,
value: [
`${Tr(T.Cores)} x ${CORES}`,
`${Tr(T.Sockets)} x ${SOCKETS}`,
].join(' | '),
dataCy: 'virtualcores',
},
{
name: T.Memory,
value: prettyBytes(+MEMORY, 'MB'),
dataCy: 'memory',
},
{
name: T.CostCpu,
value: cpuCost,
dataCy: 'cpucost',
},
{
name: T.CostMemory,
value: memoryCost,
dataCy: 'memorycost',
},
].filter(Boolean)
return <List title={<PanelHeader vm={vm} actions={actions} />} list={info} />
}
CapacityPanel.propTypes = {
actions: PropTypes.arrayOf(PropTypes.string),
vm: PropTypes.object,
}
CapacityPanel.displayName = 'CapacityPanel'
/**
* Renders header of capacity panel.
*
* @param {object} props - Props
* @param {VM} props.vm - Virtual machine
* @param {string[]} props.actions - Available actions to capacity tab
* @returns {ReactElement} Capacity panel header
*/
const PanelHeader = ({ vm = {}, actions = [] }) => {
const [resizeCapacity] = useResizeMutation()
const handleResizeCapacity = async (formData) => {
const { enforce, ...restOfData } = formData
const template = jsonToXml(restOfData)
await resizeCapacity({ id: vm.ID, enforce, template })
}
const resizeIsAvailable = useMemo(
() =>
actions
.filter((action) => isAvailableAction(action, vm))
.includes?.(VM_ACTIONS.RESIZE_CAPACITY),
[vm]
)
return (
<Stack
width={1}
direction="row"
alignItems="center"
justifyContent="space-between"
>
<Typography noWrap>
<Translate word={T.Capacity} />
</Typography>
{resizeIsAvailable && (
<ButtonToTriggerForm
buttonProps={{
'data-cy': 'resize-capacity',
icon: <EditIcon />,
tooltip: <Translate word={T.Resize} />,
}}
options={[
{
dialogProps: { title: T.ResizeCapacity },
form: () => ResizeCapacityForm({ initialValues: vm.TEMPLATE }),
onSubmit: handleResizeCapacity,
},
]}
/>
)}
</Stack>
)
}
PanelHeader.propTypes = { ...CapacityPanel.propTypes }
PanelHeader.displayName = 'PanelHeader'
export default CapacityPanel

View File

@ -30,6 +30,7 @@ import {
AttributePanel,
} from 'client/components/Tabs/Common'
import Information from 'client/components/Tabs/Vm/Info/information'
import Capacity from 'client/components/Tabs/Vm/Info/capacity'
import { SubmitButton } from 'client/components/FormControl'
import { Tr, Translate } from 'client/components/HOC'
@ -60,6 +61,7 @@ const HIDDEN_MONITORING_REG =
const VmInfoTab = ({ tabProps = {}, id }) => {
const {
information_panel: informationPanel,
capacity_panel: capacityPanel,
permissions_panel: permissionsPanel,
ownership_panel: ownershipPanel,
vcenter_panel: vcenterPanel,
@ -180,6 +182,9 @@ const VmInfoTab = ({ tabProps = {}, id }) => {
groupName={GNAME}
/>
)}
{capacityPanel?.enabled && (
<Capacity actions={getActions(capacityPanel?.actions)} vm={vm} />
)}
{attributesPanel?.enabled && attributes && (
<AttributePanel
{...ATTRIBUTE_FUNCTION}

View File

@ -23,7 +23,6 @@ import { getAvailableInfoTabs } from 'client/models/Helper'
import { RESOURCE_NAMES } from 'client/constants'
import Tabs from 'client/components/Tabs'
import Capacity from 'client/components/Tabs/Vm/Capacity'
import Configuration from 'client/components/Tabs/Vm/Configuration'
import Info from 'client/components/Tabs/Vm/Info'
import Network from 'client/components/Tabs/Vm/Network'
@ -34,7 +33,6 @@ import Storage from 'client/components/Tabs/Vm/Storage'
const getTabComponent = (tabName) =>
({
capacity: Capacity,
configuration: Configuration,
info: Info,
network: Network,