1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-23 22:50:09 +03:00

B #5903: fix vms datatable in securyty groups (#2496)

(cherry picked from commit a1d807710e0c187e7f72daf51145c80de1f17b75)
This commit is contained in:
Jorge Miguel Lobo Escalona 2023-02-13 12:45:56 +01:00 committed by Tino Vázquez
parent b2118b74a4
commit b94a12d8d0
No known key found for this signature in database
GPG Key ID: 14201E424D02047E
6 changed files with 129 additions and 32 deletions

View File

@ -13,35 +13,35 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement, memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { ReactElement, memo, useMemo } from 'react'
import { Box, Stack, Tooltip, Typography } from '@mui/material'
import {
Lock,
HardDrive,
Cpu,
HardDrive,
Lock,
Network,
WarningCircledOutline as WarningIcon,
} from 'iconoir-react'
import { Box, Stack, Typography, Tooltip } from '@mui/material'
import { useViews } from 'client/features/Auth'
import MultipleTags from 'client/components/MultipleTags'
import Timer from 'client/components/Timer'
import { MemoryIcon } from 'client/components/Icons'
import { StatusCircle, StatusChip } from 'client/components/Status'
import { Tr } from 'client/components/HOC'
import { MemoryIcon } from 'client/components/Icons'
import MultipleTags from 'client/components/MultipleTags'
import { StatusChip, StatusCircle } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import Timer from 'client/components/Timer'
import { useViews } from 'client/features/Auth'
import { getState, getLastHistory, getIps } from 'client/models/VirtualMachine'
import { ACTIONS, RESOURCE_NAMES, T, VM } from 'client/constants'
import {
timeFromMilliseconds,
getUniqueLabels,
getErrorMessage,
getColorFromString,
getErrorMessage,
getUniqueLabels,
timeFromMilliseconds,
} from 'client/models/Helper'
import { getIps, getLastHistory, getState } from 'client/models/VirtualMachine'
import { prettyBytes } from 'client/utils'
import { T, VM, ACTIONS, RESOURCE_NAMES } from 'client/constants'
const VirtualMachineCard = memo(
/**
@ -51,9 +51,17 @@ const VirtualMachineCard = memo(
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
* @param {ReactElement} [props.actions] - Actions
* @param {object[]} [props.globalErrors] - Errors globals
* @returns {ReactElement} - Card
*/
({ vm, rootProps, actions, onClickLabel, onDeleteLabel }) => {
({
vm,
rootProps,
actions,
onClickLabel,
onDeleteLabel,
globalErrors = [],
}) => {
const classes = rowStyles()
const { [RESOURCE_NAMES.VM]: vmView } = useViews()
@ -86,6 +94,31 @@ const VirtualMachineCard = memo(
name: stateName,
displayName: stateDisplayName,
} = getState(vm)
const errorRows = globalErrors.filter(
(errorRow) => errorRow?.rows?.length && errorRow?.rows?.includes(ID)
)
const IconsError = () => (
<>
{errorRows.map((value, index) => (
<Tooltip
arrow
placement="bottom"
key={`icon-${ID}-${index}`}
title={
<Typography variant="subtitle2">
{value?.message || ''}
</Typography>
}
>
<Box color={`${value?.type || 'error'}.main`} component="span">
{value?.icon || ''}
</Box>
</Tooltip>
))}
</>
)
const error = useMemo(() => getErrorMessage(vm), [vm])
const ips = useMemo(() => getIps(vm), [vm])
const memValue = useMemo(() => prettyBytes(+MEMORY, 'MB'), [MEMORY])
@ -112,6 +145,7 @@ const VirtualMachineCard = memo(
<Typography noWrap component="span">
{NAME}
</Typography>
<IconsError />
{error && (
<Tooltip
arrow
@ -171,6 +205,7 @@ VirtualMachineCard.propTypes = {
onClickLabel: PropTypes.func,
onDeleteLabel: PropTypes.func,
actions: PropTypes.any,
globalErrors: PropTypes.array,
}
VirtualMachineCard.displayName = 'VirtualMachineCard'

View File

@ -17,12 +17,12 @@
import PropTypes from 'prop-types'
import { useMemo } from 'react'
import { Box, Chip } from '@mui/material'
import { Alert, Box, Chip, Grid } from '@mui/material'
import clsx from 'clsx'
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
import RemoveIcon from 'iconoir-react/dist/RemoveSquare'
import {
// types
UseRowSelectRowProps,
useFilters,
useGlobalFilter,
@ -73,6 +73,7 @@ const EnhancedTable = ({
rootProps = {},
searchProps = {},
noDataMessage,
messages = [],
}) => {
const styles = EnhancedTableStyles()
@ -186,6 +187,26 @@ const EnhancedTable = ({
const canResetFilter = state.filters?.length > 0 || state.sortBy?.length > 0
const messageValues = messages.filter(
(messageValue) => messageValue?.rows?.length
)
const MessagesRowsAlerts = () => {
let grid = 12
messageValues.length && (grid = grid / messageValues.length)
return (
<Grid container spacing={2}>
{messageValues.map((value, index) => (
<Grid item xs={grid} key={`messageAlert-${index}`}>
<Alert icon={value?.icon || ''} severity={value?.type || 'info'}>
{value?.message || ''}
</Alert>
</Grid>
))}
</Grid>
)
}
return (
<Box
{...getTableProps()}
@ -233,7 +254,6 @@ const EnhancedTable = ({
<GlobalFilter {...useTableProps} />
{!disableGlobalSort && <GlobalSort {...useTableProps} />}
</div>
{/* SELECTED ROWS */}
{displaySelectedRows && (
<div>
@ -259,6 +279,7 @@ const EnhancedTable = ({
/>
<div className={clsx(styles.body, classes.body)}>
{messages && <MessagesRowsAlerts />}
{/* NO DATA MESSAGE */}
{!isLoading &&
!isUninitialized &&
@ -290,6 +311,9 @@ const EnhancedTable = ({
key={key}
original={original}
value={values}
{...(messageValues.length && {
globalErrors: messageValues,
})}
className={isSelected ? 'selected' : ''}
{...(!cannotFilterByLabel && {
onClickLabel: (label) => {
@ -358,6 +382,7 @@ EnhancedTable.propTypes = {
PropTypes.node,
PropTypes.bool,
]),
messages: PropTypes.array,
}
export * from 'client/components/Tables/Enhanced/Utils'

View File

@ -52,9 +52,27 @@ const VmsTable = (props) => {
...result,
data:
result?.data
?.filter((vm) =>
host?.ID ? [host?.VMS?.ID ?? []].flat().includes(vm.ID) : true
)
?.filter((vm) => {
if (host?.ID) {
if (
host?.ERROR_VMS?.ID ||
host?.UPDATED_VMS?.ID ||
host?.UPDATING_VMS?.ID
) {
return [
host?.ERROR_VMS.ID ?? [],
host?.UPDATED_VMS.ID ?? [],
host?.UPDATING_VMS.ID ?? [],
]
.flat()
.includes(vm.ID)
}
return [host?.VMS?.ID ?? []].flat().includes(vm.ID)
}
return true
})
?.filter(({ STATE }) => VM_STATES[STATE]?.name !== STATES.DONE) ?? [],
}),
})

View File

@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import vmApi, { useUpdateUserTemplateMutation } from 'client/features/OneApi/vm'
import { VirtualMachineCard } from 'client/components/Cards'
import { ConsoleButton } from 'client/components/Buttons'
import { jsonToXml } from 'client/models/Helper'
import { VirtualMachineCard } from 'client/components/Cards'
import { VM_ACTIONS } from 'client/constants'
import vmApi, { useUpdateUserTemplateMutation } from 'client/features/OneApi/vm'
import { jsonToXml } from 'client/models/Helper'
const { VNC, RDP, SSH, VMRC } = VM_ACTIONS
const CONNECTION_TYPES = [VNC, RDP, SSH, VMRC]
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, globalErrors, ...props }) => {
const [update] = useUpdateUserTemplateMutation()
const state = vmApi.endpoints.getVms.useQueryState(undefined, {
@ -54,6 +54,7 @@ const Row = memo(
rootProps={props}
onClickLabel={onClickLabel}
onDeleteLabel={handleDeleteLabel}
globalErrors={globalErrors}
actions={
<>
{CONNECTION_TYPES.map((connectionType) => (
@ -78,6 +79,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
globalErrors: PropTypes.array,
}
Row.displayName = 'VirtualMachineRow'

View File

@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement } from 'react'
import PropTypes from 'prop-types'
import { T } from 'client/constants'
import EmptyTab from 'client/components/Tabs/EmptyTab'
import { useHistory, generatePath } from 'react-router-dom'
import { PATH } from 'client/apps/sunstone/routesOne'
import { useGetSecGroupQuery } from 'client/features/OneApi/securityGroup'
import { VmsTable } from 'client/components/Tables'
import EmptyTab from 'client/components/Tabs/EmptyTab'
import { T } from 'client/constants'
import { useGetSecGroupQuery } from 'client/features/OneApi/securityGroup'
import { CloudDesync, WarningTriangleOutline } from 'iconoir-react'
import PropTypes from 'prop-types'
import { ReactElement } from 'react'
import { generatePath, useHistory } from 'react-router-dom'
/**
* Renders mainly Vms tab.
@ -43,6 +44,20 @@ const VmsTab = ({ id }) => {
disableGlobalSort
displaySelectedRows
host={secGroup}
messages={[
{
rows: [secGroup?.ERROR_VMS.ID ?? []].flat(),
message: T.ErrorUpdatingSecGroups,
icon: <WarningTriangleOutline />,
type: 'error',
},
{
rows: [secGroup?.UPDATING_VMS.ID ?? []].flat(),
message: T.PendingUpdatingSecGroups,
icon: <CloudDesync />,
type: 'info',
},
]}
onRowClick={(row) => handleRowClick(row.ID)}
noDataMessage={<EmptyTab label={T.NotVmsCurrentySecGroups} />}
/>

View File

@ -290,6 +290,8 @@ module.exports = {
NoDataAvailable: 'There is no data available',
ErrorsOcurred: '%s error(s) occurred',
UpdatedNic: 'Updated nic',
ErrorUpdatingSecGroups: 'VMs in error. The Update to the latest rules failed',
PendingUpdatingSecGroups: 'VMs waiting to be updated with the latest rules',
/* steps form */
AdvancedOptions: 'Advanced options',