mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-23 22:50:09 +03:00
(cherry picked from commit a1d807710e0c187e7f72daf51145c80de1f17b75)
This commit is contained in:
parent
b2118b74a4
commit
b94a12d8d0
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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) ?? [],
|
||||
}),
|
||||
})
|
||||
|
@ -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'
|
||||
|
@ -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} />}
|
||||
/>
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user