1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00
This commit is contained in:
Sergio Betanzos 2021-06-22 09:20:22 +02:00
parent aa83651919
commit 301da35182
No known key found for this signature in database
GPG Key ID: E3E704F097737136
5 changed files with 214 additions and 49 deletions

View File

@ -0,0 +1,11 @@
export default [
{ Header: '', accessor: 'ID' },
{ Header: '', accessor: 'NAME' },
{ Header: '', accessor: 'UNAME' },
{ Header: '', accessor: 'GNAME' },
{ Header: '', accessor: 'STATE' },
{ Header: '', accessor: 'TYPE' },
{ Header: '', accessor: 'DISK_TYPE' },
{ Header: '', accessor: 'DATASTORE_ID' },
{ Header: '', accessor: 'DATASTORE' }
]

View File

@ -0,0 +1,33 @@
import React, { useEffect } from 'react'
import { useAuth } from 'client/features/Auth'
import { useFetch } from 'client/hooks'
import { useImage, useImageApi } from 'client/features/One'
import { EnhancedTable } from 'client/components/Tables'
import ImageColumns from 'client/components/Tables/Images/columns'
import ImageRow from 'client/components/Tables/Images/row'
const ImagesTable = () => {
const columns = React.useMemo(() => ImageColumns, [])
const images = useImage()
const { getImages } = useImageApi()
const { filterPool } = useAuth()
const { fetchRequest, loading, reloading } = useFetch(getImages)
useEffect(() => { fetchRequest() }, [filterPool])
return (
<EnhancedTable
columns={columns}
data={images}
isLoading={loading || reloading}
getRowId={row => String(row.ID)}
RowComponent={ImageRow}
/>
)
}
export default ImagesTable

View File

@ -0,0 +1,77 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import { Lock, User, Group, Folder, ModernTv } from 'iconoir-react'
import { Typography } from '@material-ui/core'
import { StatusCircle, StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import * as ImageModel from 'client/models/Image'
import * as Helper from 'client/models/Helper'
const Row = ({ value, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, REGTIME, PERSISTENT, LOCK, DATASTORE, VMS, RUNNING_VMS } = value
const state = ImageModel.getState(value)
const type = ImageModel.getType(value)
const diskType = ImageModel.getDiskType(value)
const isPersistent = PERSISTENT && 'PERSISTENT'
const usedByVms = [VMS?.ID ?? []].flat().length || 0
const labels = [...new Set([isPersistent, type, diskType])].filter(Boolean)
const time = Helper.timeFromMilliseconds(+REGTIME)
const timeAgo = `registered ${time.toRelative()}`
return (
<div {...props}>
<div>
<StatusCircle color={state?.color} tooltip={state?.name} />
</div>
<div className={classes.main}>
<Typography className={classes.title} component='span'>
{NAME}
{LOCK && <Lock size={20} />}
<span className={classes.labels}>
{labels.map(label => (
<StatusChip key={label} stateColor={'#c6c6c6'} text={label} />
))}
</span>
</Typography>
<div className={classes.caption}>
<span title={time.toFormat('ff')}>
{`#${ID} ${timeAgo}`}
</span>
<span>
<User size={16} />
<span>{` ${UNAME}`}</span>
</span>
<span>
<Group size={16} />
<span>{` ${GNAME}`}</span>
</span>
<span>
<Folder size={16} />
<span>{` ${DATASTORE}`}</span>
</span>
<span>
<ModernTv size={16} />
<span>{` ${RUNNING_VMS} / ${usedByVms}`}</span>
</span>
</div>
</div>
<div className={classes.secondary}></div>
</div>
)
}
Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func
}
export default Row

View File

@ -3,71 +3,41 @@ import * as React from 'react'
import { useParams, useHistory } from 'react-router'
import { Redirect, Route, Switch, Link } from 'react-router-dom'
import { withStyles, Container, Tabs, Tab, Box } from '@material-ui/core'
import { Container, Tabs, Tab, Box } from '@material-ui/core'
import {
DatastoresTable,
HostsTable,
VmsTable
VmsTable,
MarketplacesTable,
MarketplaceAppsTable,
ImagesTable
} from 'client/components/Tables'
import { PATH } from 'client/router/dev'
const TABS = {
vms: PATH.NEWSTONE.replace(':resource', 'vms'),
datastores: PATH.NEWSTONE.replace(':resource', 'datastores'),
hosts: PATH.NEWSTONE.replace(':resource', 'hosts')
hosts: PATH.NEWSTONE.replace(':resource', 'hosts'),
marketplaces: PATH.NEWSTONE.replace(':resource', 'marketplaces'),
apps: PATH.NEWSTONE.replace(':resource', 'apps'),
images: PATH.NEWSTONE.replace(':resource', 'images')
}
const AntTabs = withStyles(theme => ({
root: {
borderBottom: '1px solid #e8e8e8'
},
indicator: {
backgroundColor: theme.palette.secondary.main
}
}))(Tabs)
const AntTab = withStyles(theme => ({
root: {
minWidth: 72,
fontWeight: theme.typography.fontWeightRegular,
marginRight: theme.spacing(4),
'&:hover': {
color: theme.palette.secondary.light,
opacity: 1
},
'&$selected': {
color: theme.palette.secondary.main,
fontWeight: theme.typography.fontWeightMedium
},
'&:focus': {
color: theme.palette.secondary.light
}
},
selected: {}
}))(props => <Tab disableRipple {...props} />)
const AntContainer = withStyles({
root: {
height: '100%',
display: 'flex',
flexDirection: 'column',
paddingInline: 0
}
})(Container)
const Newstone = () => {
const history = useHistory()
const { resource } = useParams()
const renderTabs = React.useMemo(() => (
<AntTabs
<Tabs
style={{ borderBottom: '1px solid #e8e8e8' }}
value={resource}
variant='scrollable'
scrollButtons='auto'
>
{Object.keys(TABS).map(tabName =>
<AntTab
<Tab
key={`tab-${tabName}`}
label={tabName}
value={tabName}
@ -75,23 +45,26 @@ const Newstone = () => {
to={tabName}
/>
)}
</AntTabs>
</Tabs>
), [resource])
return (
<AntContainer>
<Container>
{Object.values(TABS).includes(history.location.pathname) && renderTabs}
<Box py={2} overflow='auto'>
<Switch>
<Route path={TABS.vms} component={VmsTable} />
<Route path={TABS.datastores} component={DatastoresTable} />
<Route path={TABS.hosts} component={HostsTable} />
<Route exact path={TABS.vms} component={VmsTable} />
<Route exact path={TABS.datastores} component={DatastoresTable} />
<Route exact path={TABS.hosts} component={HostsTable} />
<Route exact path={TABS.marketplaces} component={MarketplacesTable} />
<Route exact path={TABS.apps} component={MarketplaceAppsTable} />
<Route exact path={TABS.images} component={ImagesTable} />
<Route component={() => <Redirect to={TABS.vms} />} />
</Switch>
</Box>
</AntContainer>
</Container>
)
}

View File

@ -0,0 +1,71 @@
import * as STATES from 'client/constants/states'
import COLOR from 'client/constants/color'
const IMAGE_TYPES = [
'OS',
'CD ROM',
'DATABLOCK',
'KERNEL',
'RAMDISK',
'CONTEXT'
]
const IMAGE_STATES = [
{ // 0
name: STATES.INIT,
color: COLOR.debug.main
},
{ // 1
name: STATES.READY,
color: COLOR.success.main
},
{ // 2
name: STATES.USED,
color: COLOR.success.main
},
{ // 3
name: STATES.DISABLED,
color: COLOR.debug.light
},
{ // 4
name: STATES.LOCKED,
color: COLOR.warning.main
},
{ // 5
name: STATES.ERROR,
color: COLOR.error.main
},
{ // 6
name: STATES.CLONE,
color: COLOR.info.light
},
{ // 7
name: STATES.DELETE,
color: COLOR.error.main
},
{ // 8
name: STATES.USED_PERS,
color: COLOR.error.light
},
{ // 9
name: STATES.LOCKED_USED,
color: COLOR.warning.light
},
{ // 10
name: STATES.LOCKED_USED_PERS,
color: COLOR.error.light
}
]
const DISK_TYPES = [
'FILE',
'CD ROM',
'BLOCK',
'RBD'
]
export const getType = ({ TYPE } = {}) => IMAGE_TYPES[+TYPE]
export const getDiskType = ({ DISK_TYPE } = {}) => DISK_TYPES[+DISK_TYPE]
export const getState = ({ STATE } = {}) => IMAGE_STATES[+STATE]