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

F #5422: Add threshold to bars (#2009)

This commit is contained in:
Sergio Betanzos 2022-05-11 12:41:23 +02:00 committed by GitHub
parent 2e63fc77f0
commit f5efac3ffe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 281 additions and 97 deletions

View File

@ -13,21 +13,30 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, ReactElement } from 'react'
import { memo, ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import { User, Group, Lock, Cloud, Server } from 'iconoir-react'
import {
User,
Group,
Lock,
Cloud,
Server,
WarningCircledOutline as WarningIcon,
} from 'iconoir-react'
import { Box, Typography, Tooltip } from '@mui/material'
import {
StatusCircle,
StatusChip,
LinearProgressWithLabel,
} from 'client/components/Status'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { getState, getType, getCapacityInfo } from 'client/models/Datastore'
import { rowStyles } from 'client/components/Tables/styles'
import { Datastore } from 'client/constants'
import { getErrorMessage } from 'client/models/Helper'
import { T, Datastore, DS_THRESHOLD } from 'client/constants'
const DatastoreCard = memo(
/**
@ -37,15 +46,21 @@ const DatastoreCard = memo(
* @param {ReactElement} props.actions - Actions
* @returns {ReactElement} - Card
*/
({ datastore, rootProps, actions }) => {
({ datastore: ds, rootProps, actions }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, CLUSTERS, LOCK, PROVISION_ID } = datastore
const { ID, NAME, UNAME, GNAME, CLUSTERS, LOCK, PROVISION_ID } = ds
const type = getType(datastore)
const { color: stateColor, name: stateName } = getState(datastore)
const { percentOfUsed, percentLabel } = getCapacityInfo(datastore)
const totalClusters = [CLUSTERS?.ID ?? []].flat().join(',')
const type = getType(ds)
const { color: stateColor, name: stateName } = getState(ds)
const error = useMemo(() => getErrorMessage(ds), [ds])
const capacity = useMemo(() => getCapacityInfo(ds), [ds])
const { percentOfUsed, percentLabel } = capacity
const clusters = useMemo(
() => [CLUSTERS?.ID ?? []].flat().join(),
[CLUSTERS?.ID]
)
return (
<div {...rootProps} data-cy={`datastore-${ID}`}>
@ -53,6 +68,17 @@ const DatastoreCard = memo(
<div className={classes.title}>
<StatusCircle color={stateColor} tooltip={stateName} />
<Typography component="span">{NAME}</Typography>
{error && (
<Tooltip
arrow
placement="bottom"
title={<Typography variant="subtitle2">{error}</Typography>}
>
<Box color="error.dark" component="span">
<WarningIcon />
</Box>
</Tooltip>
)}
<span className={classes.labels}>
{LOCK && <Lock />}
<StatusChip text={type} />
@ -60,28 +86,34 @@ const DatastoreCard = memo(
</div>
<div className={classes.caption}>
<span>{`#${ID}`}</span>
<span title={`Owner: ${UNAME}`}>
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
<User />
<span>{` ${UNAME}`}</span>
</span>
<span title={`Group: ${GNAME}`}>
<span title={`${Tr(T.Group)}: ${GNAME}`}>
<Group />
<span>{` ${GNAME}`}</span>
</span>
{PROVISION_ID && (
<span title={`Provision ID: #${PROVISION_ID}`}>
<span title={`${Tr(T.ProvisionId)}: #${PROVISION_ID}`}>
<Cloud />
<span>{` ${PROVISION_ID}`}</span>
</span>
)}
<span title={`Cluster IDs: ${totalClusters}`}>
<span title={`${Tr(T.Clusters)}: ${clusters}`}>
<Server />
<span>{` ${totalClusters}`}</span>
<span>{` ${clusters}`}</span>
</span>
</div>
</div>
<div className={classes.secondary}>
<LinearProgressWithLabel value={percentOfUsed} label={percentLabel} />
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={DS_THRESHOLD.CAPACITY.high}
low={DS_THRESHOLD.CAPACITY.low}
title={Tr(T.UsedOfTotal)}
/>
</div>
{actions && <div className={classes.actions}>{actions}</div>}
</div>

View File

@ -28,7 +28,7 @@ import { rowStyles } from 'client/components/Tables/styles'
import { Tr } from 'client/components/HOC'
import { getAllocatedInfo, getState } from 'client/models/Host'
import { T, Host } from 'client/constants'
import { T, Host, HOST_THRESHOLD } from 'client/constants'
const HostCard = memo(
/**
@ -80,15 +80,15 @@ const HostCard = memo(
<div className={classes.secondary}>
<LinearProgressWithLabel
value={percentCpuUsed}
high={66}
low={33}
high={HOST_THRESHOLD.CPU.high}
low={HOST_THRESHOLD.CPU.low}
label={percentCpuLabel}
title={`${Tr(T.AllocatedCpu)}`}
/>
<LinearProgressWithLabel
value={percentMemUsed}
high={66}
low={33}
high={HOST_THRESHOLD.MEMORY.high}
low={HOST_THRESHOLD.MEMORY.low}
label={percentMemLabel}
title={`${Tr(T.AllocatedMemory)}`}
/>

View File

@ -0,0 +1,124 @@
/* ------------------------------------------------------------------------- *
* 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 { memo, ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import {
User,
Group,
Server,
WarningCircledOutline as WarningIcon,
} from 'iconoir-react'
import { Box, Typography, Tooltip } from '@mui/material'
import {
StatusCircle,
StatusChip,
LinearProgressWithLabel,
} from 'client/components/Status'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { getState, getCapacityInfo } from 'client/models/Datastore'
import { getErrorMessage } from 'client/models/Helper'
import { T, Marketplace, MARKET_THRESHOLD } from 'client/constants'
const MarketplaceCard = memo(
/**
* @param {object} props - Props
* @param {Marketplace} props.market - Marketplace resource
* @param {object} props.rootProps - Props to root component
* @param {ReactElement} props.actions - Actions
* @returns {ReactElement} - Card
*/
({ market, rootProps, actions }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, MARKET_MAD, MARKETPLACEAPPS } = market
const { color: stateColor, name: stateName } = getState(market)
const error = useMemo(() => getErrorMessage(market), [market])
const capacity = useMemo(() => getCapacityInfo(market), [market])
const { percentOfUsed, percentLabel } = capacity
const apps = useMemo(
() => [MARKETPLACEAPPS?.ID ?? []].flat().length || 0,
[MARKETPLACEAPPS?.ID]
)
return (
<div {...rootProps} data-cy={`datastore-${ID}`}>
<div className={classes.main}>
<div className={classes.title}>
<StatusCircle color={stateColor} tooltip={stateName} />
<Typography component="span">{NAME}</Typography>
{error && (
<Tooltip
arrow
placement="bottom"
title={<Typography variant="subtitle2">{error}</Typography>}
>
<Box color="error.dark" component="span">
<WarningIcon />
</Box>
</Tooltip>
)}
<span className={classes.labels}>
<StatusChip text={MARKET_MAD} />
</span>
</div>
<div className={classes.caption}>
<span>{`#${ID}`}</span>
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
<User />
<span>{` ${UNAME}`}</span>
</span>
<span title={`${Tr(T.Group)}: ${GNAME}`}>
<Group />
<span>{` ${GNAME}`}</span>
</span>
<span title={`${Tr(T.Apps)}: ${apps}`}>
<Server />
<span>{` ${apps}`}</span>
</span>
</div>
</div>
<div className={classes.secondary}>
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={MARKET_THRESHOLD.CAPACITY.high}
low={MARKET_THRESHOLD.CAPACITY.low}
title={Tr(T.UsedOfTotal)}
/>
</div>
{actions && <div className={classes.actions}>{actions}</div>}
</div>
)
}
)
MarketplaceCard.propTypes = {
market: PropTypes.object,
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
actions: PropTypes.any,
}
MarketplaceCard.displayName = 'MarketplaceCard'
export default MarketplaceCard

View File

@ -23,6 +23,7 @@ import DiskSnapshotCard from 'client/components/Cards/DiskSnapshotCard'
import EmptyCard from 'client/components/Cards/EmptyCard'
import HostCard from 'client/components/Cards/HostCard'
import MarketplaceAppCard from 'client/components/Cards/MarketplaceAppCard'
import MarketplaceCard from 'client/components/Cards/MarketplaceCard'
import NetworkCard from 'client/components/Cards/NetworkCard'
import NicCard from 'client/components/Cards/NicCard'
import PolicyCard from 'client/components/Cards/PolicyCard'
@ -48,6 +49,7 @@ export {
EmptyCard,
HostCard,
MarketplaceAppCard,
MarketplaceCard,
NetworkCard,
NicCard,
PolicyCard,

View File

@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import { CategoryFilter } from 'client/components/Tables/Enhanced/Utils'
import * as MarketplaceModel from 'client/models/Datastore'
import { getState } from 'client/models/Datastore'
const getTotalOfResources = (resources) =>
[resources?.ID ?? []].flat().length || 0
@ -28,7 +28,7 @@ export default [
{
Header: 'State',
id: 'STATE',
accessor: (row) => MarketplaceModel.getState(row)?.name,
accessor: (row) => getState(row)?.name,
disableFilters: false,
Filter: ({ column }) =>
CategoryFilter({

View File

@ -13,69 +13,37 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { User, Group, CloudDownload } from 'iconoir-react'
import { Typography } from '@mui/material'
import marketplaceApi from 'client/features/OneApi/marketplace'
import { MarketplaceCard } from 'client/components/Cards'
import {
StatusCircle,
LinearProgressWithLabel,
StatusChip,
} from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
const Row = memo(
({ original, value, ...props }) => {
const state = marketplaceApi.endpoints.getMarketplaces.useQueryState(
undefined,
{
selectFromResult: ({ data = [] }) =>
data.find((market) => +market?.ID === +original.ID),
}
)
import * as MarketplaceModel from 'client/models/Datastore'
const memoMarket = useMemo(() => state ?? original, [state, original])
const Row = ({ original, value, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, MARKET_MAD, TOTAL_APPS } = value
const { name: stateName, color: stateColor } =
MarketplaceModel.getState(original)
const { percentOfUsed, percentLabel } =
MarketplaceModel.getCapacityInfo(value)
return (
<div {...props}>
<div className={classes.main}>
<div className={classes.title}>
<StatusCircle color={stateColor} tooltip={stateName} />
<Typography component="span">{NAME}</Typography>
<span className={classes.labels}>
<StatusChip text={MARKET_MAD} />
</span>
</div>
<div className={classes.caption}>
<span>{`#${ID}`}</span>
<span title={`Owner: ${UNAME}`}>
<User />
<span>{` ${UNAME}`}</span>
</span>
<span title={`Group: ${GNAME}`}>
<Group />
<span>{` ${GNAME}`}</span>
</span>
<span title={`Total Apps: ${TOTAL_APPS}`}>
<CloudDownload />
<span>{` ${TOTAL_APPS}`}</span>
</span>
</div>
</div>
<div className={classes.secondary}>
<LinearProgressWithLabel value={percentOfUsed} label={percentLabel} />
</div>
</div>
)
}
return <MarketplaceCard market={memoMarket} rootProps={props} />
},
(prev, next) => prev.className === next.className
)
Row.propTypes = {
value: PropTypes.object,
original: PropTypes.object,
value: PropTypes.object,
isSelected: PropTypes.bool,
className: PropTypes.string,
handleClick: PropTypes.func,
}
Row.displayName = 'MarketplaceRow'
export default Row

View File

@ -23,7 +23,7 @@ import { List } from 'client/components/Tabs/Common'
import { getState, getType, getCapacityInfo } from 'client/models/Datastore'
import { stringToBoolean } from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, Datastore, DATASTORE_ACTIONS } from 'client/constants'
import { T, Datastore, DATASTORE_ACTIONS, DS_THRESHOLD } from 'client/constants'
/**
* Renders mainly information tab.
@ -69,7 +69,12 @@ const InformationPanel = ({ datastore = {}, actions }) => {
{
name: T.Capacity,
value: (
<LinearProgressWithLabel value={percentOfUsed} label={percentLabel} />
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={DS_THRESHOLD.CAPACITY.high}
low={DS_THRESHOLD.CAPACITY.low}
/>
),
dataCy: 'capacity',
},

View File

@ -24,7 +24,13 @@ import { List } from 'client/components/Tabs/Common'
import { getState, getDatastores, getAllocatedInfo } from 'client/models/Host'
import { getCapacityInfo } from 'client/models/Datastore'
import { T, VM_ACTIONS, Host } from 'client/constants'
import {
T,
VM_ACTIONS,
Host,
HOST_THRESHOLD,
DS_THRESHOLD,
} from 'client/constants'
import { PATH } from 'client/apps/sunstone/routesOne'
/**
@ -71,26 +77,30 @@ const InformationPanel = ({ host = {}, actions }) => {
generatePath(PATH.INFRASTRUCTURE.CLUSTERS.DETAIL, { id: CLUSTER_ID }),
dataCy: 'clusterid',
},
{ name: T.IM_MAD, value: IM_MAD, dataCy: 'immad' },
{ name: T.VM_MAD, value: VM_MAD, dataCy: 'vmmad' },
{ name: T.IM_MAD, value: <StatusChip text={IM_MAD} />, dataCy: 'immad' },
{ name: T.VM_MAD, value: <StatusChip text={VM_MAD} />, dataCy: 'vmmad' },
]
const capacity = [
{
name: T.AllocatedMemory,
value: (
<LinearProgressWithLabel
value={percentMemUsed}
label={percentMemLabel}
/>
),
},
{
name: T.AllocatedCpu,
value: (
<LinearProgressWithLabel
value={percentCpuUsed}
label={percentCpuLabel}
high={HOST_THRESHOLD.CPU.high}
low={HOST_THRESHOLD.CPU.low}
/>
),
},
{
name: T.AllocatedMemory,
value: (
<LinearProgressWithLabel
value={percentMemUsed}
label={percentMemLabel}
high={HOST_THRESHOLD.MEMORY.high}
low={HOST_THRESHOLD.MEMORY.low}
/>
),
},
@ -104,7 +114,12 @@ const InformationPanel = ({ host = {}, actions }) => {
name: `#${dsHost.ID} ${dsName}`,
dataCy: `ds-id-${dsHost.ID}`,
value: (
<LinearProgressWithLabel value={percentOfUsed} label={percentLabel} />
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={DS_THRESHOLD.CAPACITY.high}
low={DS_THRESHOLD.CAPACITY.low}
/>
),
}
})

View File

@ -21,7 +21,12 @@ import { StatusChip, LinearProgressWithLabel } from 'client/components/Status'
import { List } from 'client/components/Tabs/Common'
import { getState, getCapacityInfo } from 'client/models/Marketplace'
import { T, Marketplace, MARKETPLACE_ACTIONS } from 'client/constants'
import {
T,
Marketplace,
MARKETPLACE_ACTIONS,
MARKET_THRESHOLD,
} from 'client/constants'
/**
* Renders mainly information tab.
@ -54,7 +59,7 @@ const InformationPanel = ({ marketplace = {}, actions }) => {
},
{
name: T.Driver,
value: MARKET_MAD,
value: <StatusChip text={MARKET_MAD} />,
dataCy: 'market_mad',
},
{
@ -65,7 +70,12 @@ const InformationPanel = ({ marketplace = {}, actions }) => {
{
name: T.Capacity,
value: (
<LinearProgressWithLabel value={percentOfUsed} label={percentLabel} />
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={MARKET_THRESHOLD.CAPACITY.high}
low={MARKET_THRESHOLD.CAPACITY.low}
/>
),
dataCy: 'capacity',
},

View File

@ -77,7 +77,7 @@ const InformationPanel = ({ app = {}, actions }) => {
name: T.StartTime,
value: timeToString(REGTIME),
},
{ name: T.Type, value: typeName },
{ name: T.Type, value: <StatusChip text={typeName} /> },
{ name: T.Size, value: prettyBytes(SIZE, 'MB') },
{
name: T.State,

View File

@ -81,3 +81,11 @@ export const DATASTORE_ACTIONS = {
CHANGE_OWNER: ACTIONS.CHANGE_OWNER,
CHANGE_GROUP: ACTIONS.CHANGE_GROUP,
}
/**
* @enum {{ high: number, low: number }}
* Datastore threshold to specify the maximum and minimum of the bar range
*/
export const DS_THRESHOLD = {
CAPACITY: { high: 66, low: 33 },
}

View File

@ -196,8 +196,17 @@ export const PIN_POLICY = {
PINNED: 'PINNED',
}
/** @type {object} Custom Hypervisor */
/** @enum {string} Custom Hypervisor */
export const CUSTOM_HOST_HYPERVISOR = {
NAME: 'Custom',
SUNSTONE_NAME: T.CustomHypervisor,
}
/**
* @enum {{ high: number, low: number }}
* Host threshold to specify the maximum and minimum of the bar range
*/
export const HOST_THRESHOLD = {
CPU: { high: 66, low: 33 },
MEMORY: { high: 90, low: 40 },
}

View File

@ -112,3 +112,11 @@ export const MARKETPLACE_ACTIONS = {
CHANGE_OWNER: ACTIONS.CHANGE_OWNER,
CHANGE_GROUP: ACTIONS.CHANGE_GROUP,
}
/**
* @enum {{ high: number, low: number }}
* Marketplace threshold to specify the maximum and minimum of the bar range
*/
export const MARKET_THRESHOLD = {
CAPACITY: { high: 66, low: 33 },
}

View File

@ -383,6 +383,7 @@ module.exports = {
Format: 'Format',
Prefix: 'Prefix',
More: 'More',
UsedOfTotal: 'Used / Total',
/* permissions */
Permissions: 'Permissions',

View File

@ -258,3 +258,5 @@ export const {
useEnableMarketplaceMutation,
useDisableMarketplaceMutation,
} = marketplaceApi
export default marketplaceApi