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

F OpenNebula/one#5422: Add vrouters table

This commit is contained in:
Sergio Betanzos 2021-07-01 12:49:24 +02:00
parent e25b2ad18b
commit bda9ecedcc
No known key found for this signature in database
GPG Key ID: E3E704F097737136
17 changed files with 253 additions and 23 deletions

View File

@ -4,6 +4,7 @@ import {
Cell4x4 as InstancesIcons,
ModernTv as VmsIcons,
Shuffle as VRoutersIcons,
Archive as TemplatesIcon,
GoogleDocs as TemplateIcon,
@ -35,7 +36,7 @@ const Dashboard = loadable(() => import('client/containers/Dashboard/Sunstone'),
const Settings = loadable(() => import('client/containers/Settings'), { ssr: false })
const VirtualMachines = loadable(() => import('client/containers/VirtualMachines'), { ssr: false })
// const VirtualRouters = loadable(() => import('client/containers/VirtualRouters'), { ssr: false })
const VirtualRouters = loadable(() => import('client/containers/VirtualRouters'), { ssr: false })
const VmTemplates = loadable(() => import('client/containers/VmTemplates'), { ssr: false })
// const VrTemplates = loadable(() => import('client/containers/VrTemplates'), { ssr: false })
@ -66,6 +67,9 @@ export const PATH = {
INSTANCE: {
VMS: {
LIST: '/vms'
},
VROUTERS: {
LIST: '/virtual-routers'
}
},
TEMPLATE: {
@ -142,6 +146,13 @@ export const ENDPOINTS = [
sidebar: true,
icon: VmsIcons,
Component: VirtualMachines
},
{
label: 'Virtual Routers',
path: PATH.INSTANCE.VROUTERS.LIST,
sidebar: true,
icon: VRoutersIcons,
Component: VirtualRouters
}
]
},

View File

@ -60,7 +60,7 @@ const CategoryFilter = ({ title, column, accessorOption, multiple }) => {
>
{Tr(title)}
{isFiltered && (
<IconButton disableRipple disablePadding size='small' onClick={handleClear}>
<IconButton disableRipple size='small' onClick={handleClear}>
<Cancel/>
</IconButton>
)}

View File

@ -0,0 +1,19 @@
const getTotalOfResources = resources => [resources?.ID ?? []].flat().length || 0
export default [
{ Header: 'ID', accessor: 'ID', sortType: 'number' },
{ Header: 'Name', accessor: 'NAME' },
{ Header: 'Owner', accessor: 'UNAME' },
{ Header: 'Group', accessor: 'GNAME' },
{
Header: 'Total VMs',
id: 'VMS',
accessor: row => getTotalOfResources(row?.VMS),
sortType: 'number'
},
{
Header: 'Group',
id: 'TEMPLATE_ID',
accessor: row => row?.TEMPLATE?.TEMPLATE_ID
}
]

View File

@ -0,0 +1,36 @@
import React, { useEffect } from 'react'
import { useFetch } from 'client/hooks'
import { useVRouter, useVRouterApi } from 'client/features/One'
import { SkeletonTable, EnhancedTable } from 'client/components/Tables'
import VRouterColumns from 'client/components/Tables/VRouters/columns'
import VRouterRow from 'client/components/Tables/VRouters/row'
const VRoutersTable = () => {
const columns = React.useMemo(() => VRouterColumns, [])
const vRouters = useVRouter()
const { getVRouters } = useVRouterApi()
const { status, fetchRequest, loading, reloading, STATUS } = useFetch(getVRouters)
const { INIT, PENDING } = STATUS
useEffect(() => { fetchRequest() }, [])
if (vRouters?.length === 0 && [INIT, PENDING].includes(status)) {
return <SkeletonTable />
}
return (
<EnhancedTable
columns={columns}
data={vRouters}
isLoading={loading || reloading}
getRowId={row => String(row.ID)}
RowComponent={VRouterRow}
/>
)
}
export default VRoutersTable

View File

@ -0,0 +1,54 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import { User, Group, EmptyPage, ModernTv } from 'iconoir-react'
import { Typography } from '@material-ui/core'
import { rowStyles } from 'client/components/Tables/styles'
const Row = ({ original, value, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, VMS, TEMPLATE_ID } = value
return (
<div {...props}>
<div className={classes.main}>
<div className={classes.title}>
<Typography className={classes.titleText} component='span'>
{NAME}
</Typography>
</div>
<div className={classes.caption}>
<span>
{`#${ID}`}
</span>
<span title={`Owner: ${UNAME}`}>
<User size={16} />
<span>{` ${UNAME}`}</span>
</span>
<span title={`Group: ${GNAME}`}>
<Group size={16} />
<span>{` ${GNAME}`}</span>
</span>
<span title={`Template ID: ${TEMPLATE_ID}`}>
<EmptyPage size={16} />
<span>{` ${TEMPLATE_ID}`}</span>
</span>
<span title={`Total VMs: ${VMS}`}>
<ModernTv size={16} />
<span>{` ${VMS}`}</span>
</span>
</div>
</div>
</div>
)
}
Row.propTypes = {
original: PropTypes.object,
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func
}
export default Row

View File

@ -1,8 +1,23 @@
import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter'
import * as Helper from 'client/models/Helper'
export default [
{ Header: 'ID', accessor: 'ID', sortType: 'number' },
{ Header: 'Name', accessor: 'NAME' },
{ Header: 'Owner', accessor: 'UNAME' },
{ Header: 'Group', accessor: 'GNAME' },
{ Header: 'Start Time', accessor: 'REGTIME' },
{ Header: 'Locked', accessor: 'LOCK' }
{ Header: 'Locked', accessor: 'LOCK' },
{
Header: 'Virtual Router',
id: 'VROUTER',
accessor: row =>
Helper.stringToBoolean(row?.TEMPLATE?.VROUTER) && 'VROUTER',
disableFilters: false,
Filter: ({ column }) => CategoryFilter({
column,
title: 'Virtual Router'
}),
filter: 'exact'
}
]

View File

@ -4,13 +4,14 @@ import PropTypes from 'prop-types'
import { User, Group, Lock } from 'iconoir-react'
import { Typography } from '@material-ui/core'
import { StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import * as Helper from 'client/models/Helper'
const Row = ({ original, value, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, REGTIME, LOCK } = value
const { ID, NAME, UNAME, GNAME, REGTIME, LOCK, VROUTER } = value
const time = Helper.timeFromMilliseconds(+REGTIME)
const timeAgo = `registered ${time.toRelative()}`
@ -24,6 +25,7 @@ const Row = ({ original, value, ...props }) => {
</Typography>
<span className={classes.labels}>
{LOCK && <Lock size={20} />}
{VROUTER && <StatusChip stateColor={'#c6c6c6'} text={VROUTER} />}
</span>
</div>
<div className={classes.caption}>

View File

@ -13,7 +13,8 @@ import VmsTable from 'client/components/Tables/Vms'
import VmTemplatesTable from 'client/components/Tables/VmTemplates'
import VNetworksTable from 'client/components/Tables/VNetworks'
import VNetworkTemplatesTable from 'client/components/Tables/VNetworkTemplates'
import ZoneTable from 'client/components/Tables/Zones'
import VRoutersTable from 'client/components/Tables/VRouters'
import ZonesTable from 'client/components/Tables/Zones'
export {
SkeletonTable,
@ -32,5 +33,6 @@ export {
VmTemplatesTable,
VNetworksTable,
VNetworkTemplatesTable,
ZoneTable
VRoutersTable,
ZonesTable
}

View File

@ -0,0 +1,22 @@
import * as React from 'react'
import { Container, Box } from '@material-ui/core'
import * as Tables from 'client/components/Tables'
function VirtualRouters () {
return (
<Box
height={1}
py={2}
overflow='auto'
display='flex'
flexDirection='column'
component={Container}
>
<Tables.VRoutersTable />
</Box>
)
}
export default VirtualRouters

View File

@ -14,7 +14,7 @@ function Zones () {
flexDirection='column'
component={Container}
>
<Tables.ZoneTable />
<Tables.ZonesTable />
</Box>
)
}

View File

@ -20,4 +20,5 @@ export * from 'client/features/One/vm/hooks'
export * from 'client/features/One/vmTemplate/hooks'
export * from 'client/features/One/vnetwork/hooks'
export * from 'client/features/One/vnetworkTemplate/hooks'
export * from 'client/features/One/vrouter/hooks'
export * from 'client/features/One/zone/hooks'

View File

@ -27,6 +27,7 @@ const RESOURCES = {
vmgroup: 'vmGroups',
vn: 'vNetworks',
vntemplate: 'vNetworksTemplates',
vrouter: 'vRouters',
zone: 'zones',
document: {
100: 'applications',
@ -58,6 +59,7 @@ const initial = {
[RESOURCES.vmgroup]: [],
[RESOURCES.vn]: [],
[RESOURCES.vntemplate]: [],
[RESOURCES.vrouter]: [],
[RESOURCES.zone]: [],
[RESOURCES.document[100]]: [],
[RESOURCES.document[101]]: [],

View File

@ -0,0 +1,14 @@
import { createAction } from 'client/features/One/utils'
import { vRouterService } from 'client/features/One/vrouter/services'
import { RESOURCES } from 'client/features/One/slice'
export const getVRouter = createAction(
'vrouter/detail',
vRouterService.getVRouter
)
export const getVRouters = createAction(
'vrouter/pool',
vRouterService.getVRouters,
response => ({ [RESOURCES.vrouter]: response })
)

View File

@ -0,0 +1,24 @@
import { useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/vrouter/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useVRouter = () => (
useSelector(state => state.one[RESOURCES.vrouter])
)
export const useVRouterApi = () => {
const dispatch = useDispatch()
const unwrapDispatch = useCallback(
action => dispatch(action).then(unwrapResult)
, [dispatch]
)
return {
getVRouter: id => unwrapDispatch(actions.getVRouter({ id })),
getVRouters: () => unwrapDispatch(actions.getVRouters())
}
}

View File

@ -0,0 +1,25 @@
import { Actions, Commands } from 'server/utils/constants/commands/vrouter'
import { httpCodes } from 'server/utils/constants'
import { requestParams, RestClient } from 'client/utils'
import { poolRequest } from 'client/features/One/utils'
export const vRouterService = ({
getVRouter: ({ filter, id }) => {
const name = Actions.VROUTER_INFO
const { url, options } = requestParams(
{ filter, id },
{ name, ...Commands[name] }
)
return RestClient.get(url, options).then(res => {
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
return res?.data?.VROUTER ?? {}
})
},
getVRouters: data => {
const name = Actions.VROUTER_POOL_INFO
const command = { name, ...Commands[name] }
return poolRequest(data, command, 'VROUTER')
}
})

View File

@ -2,6 +2,9 @@ import { DateTime } from 'luxon'
export const booleanToString = bool => bool ? 'Yes' : 'No'
export const stringToBoolean = str =>
String(str).toLowerCase() === 'yes' || +str === 1
export const timeToString = time =>
+time ? new Date(+time * 1000).toLocaleString() : '-'

View File

@ -16,21 +16,21 @@
const {
from: { resource, postBody, query },
httpMethod: { GET, POST, PUT, DELETE }
} = require('../defaults');
} = require('../defaults')
const VROUTER_ALLOCATE = 'vrouter.allocate';
const VROUTER_DELETE = 'vrouter.delete';
const VROUTER_INSTANTIATE = 'vrouter.instantiate';
const VROUTER_NIC_ATTACH = 'vrouter.attachnic';
const VROUTER_NIC_DETACH = 'vrouter.detachnic';
const VROUTER_UPDATE = 'vrouter.update';
const VROUTER_CHMOD = 'vrouter.chmod';
const VROUTER_CHOWN = 'vrouter.chown';
const VROUTER_RENAME = 'vrouter.rename';
const VROUTER_INFO = 'vrouter.info';
const VROUTER_LOCK = 'vrouter.lock';
const VROUTER_UNLOCK = 'vrouter.unlock';
const VROUTER_POOL_INFO = 'vrouterpool.info';
const VROUTER_ALLOCATE = 'vrouter.allocate'
const VROUTER_DELETE = 'vrouter.delete'
const VROUTER_INSTANTIATE = 'vrouter.instantiate'
const VROUTER_NIC_ATTACH = 'vrouter.attachnic'
const VROUTER_NIC_DETACH = 'vrouter.detachnic'
const VROUTER_UPDATE = 'vrouter.update'
const VROUTER_CHMOD = 'vrouter.chmod'
const VROUTER_CHOWN = 'vrouter.chown'
const VROUTER_RENAME = 'vrouter.rename'
const VROUTER_INFO = 'vrouter.info'
const VROUTER_LOCK = 'vrouter.lock'
const VROUTER_UNLOCK = 'vrouter.unlock'
const VROUTER_POOL_INFO = 'vrouterpool.info'
const Actions = {
VROUTER_ALLOCATE,
@ -46,7 +46,7 @@ const Actions = {
VROUTER_LOCK,
VROUTER_UNLOCK,
VROUTER_POOL_INFO
};
}
module.exports = {
Actions,
@ -286,4 +286,4 @@ module.exports = {
}
}
}
};
}