mirror of
https://github.com/OpenNebula/one.git
synced 2025-02-10 13:57:22 +03:00
F OpenNebula/one#5422: Add styles to nics tabs
This commit is contained in:
parent
05e706b2b5
commit
b75e5d7181
@ -19,9 +19,15 @@ import PropTypes from 'prop-types'
|
||||
import useFetch from 'client/hooks/useFetch'
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
|
||||
const Action = memo(({ handleClick, icon, cy, ...props }) => {
|
||||
const Action = memo(({
|
||||
cy,
|
||||
handleClick,
|
||||
icon,
|
||||
stopPropagation,
|
||||
...props
|
||||
}) => {
|
||||
const { fetchRequest, data, loading } = useFetch(
|
||||
() => Promise.resolve(handleClick?.())
|
||||
e => Promise.resolve(handleClick?.(e))
|
||||
)
|
||||
|
||||
return (
|
||||
@ -30,7 +36,10 @@ const Action = memo(({ handleClick, icon, cy, ...props }) => {
|
||||
icon={!!icon}
|
||||
isSubmitting={loading}
|
||||
label={icon}
|
||||
onClick={fetchRequest}
|
||||
onClick={evt => {
|
||||
stopPropagation && evt?.stopPropagation?.()
|
||||
fetchRequest()
|
||||
}}
|
||||
disabled={!!data}
|
||||
{...props}
|
||||
/>
|
||||
@ -38,9 +47,10 @@ const Action = memo(({ handleClick, icon, cy, ...props }) => {
|
||||
})
|
||||
|
||||
Action.propTypes = {
|
||||
cy: PropTypes.string,
|
||||
handleClick: PropTypes.func.isRequired,
|
||||
icon: PropTypes.node.isRequired,
|
||||
cy: PropTypes.string
|
||||
stopPropagation: PropTypes.bool
|
||||
}
|
||||
|
||||
Action.defaultProps = {
|
||||
|
@ -28,17 +28,24 @@ const Multiple = ({ tags, limitTags = 1 }) => {
|
||||
|
||||
const more = tags.length - limitTags
|
||||
|
||||
const Tags = tags.splice(0, limitTags).map(tag => (
|
||||
<StatusChip key={tag} text={tag} />
|
||||
))
|
||||
const Tags = tags
|
||||
.splice(0, limitTags)
|
||||
.map((tag, idx) => <StatusChip key={`${idx}-${tag}`} text={tag} />)
|
||||
|
||||
return (
|
||||
<>
|
||||
{Tags}
|
||||
{more > 0 && (
|
||||
<Tooltip arrow
|
||||
title={tags.map(tag => (
|
||||
<Typography key={tag} variant='subtitle2'>{tag}</Typography>
|
||||
<Tooltip
|
||||
arrow
|
||||
title={tags.map((tag, idx) => (
|
||||
<Typography
|
||||
key={`${idx}-${tag}`}
|
||||
variant='subtitle2'
|
||||
style={{ height: 'max-content' }}
|
||||
>
|
||||
{tag}
|
||||
</Typography>
|
||||
))}
|
||||
>
|
||||
<span style={{ marginLeft: 6 }}>
|
||||
|
@ -23,9 +23,7 @@ import { prettyBytes } from 'client/utils'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
padding: '1em'
|
||||
},
|
||||
grid: {
|
||||
marginBlock: '0.8em',
|
||||
padding: '1em',
|
||||
display: 'grid',
|
||||
gap: '1em',
|
||||
@ -80,30 +78,28 @@ const VmCapacityTab = data => {
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Paper variant='outlined' className={classes.grid}>
|
||||
<div className={classes.actions}>
|
||||
<Action
|
||||
cy='resize'
|
||||
icon={false}
|
||||
label={'Resize'}
|
||||
size='small'
|
||||
color='secondary'
|
||||
handleClick={() => undefined}
|
||||
/>
|
||||
<Paper variant='outlined' className={classes.root}>
|
||||
<div className={classes.actions}>
|
||||
<Action
|
||||
cy='resize'
|
||||
icon={false}
|
||||
label={'Resize'}
|
||||
size='small'
|
||||
color='secondary'
|
||||
handleClick={() => undefined}
|
||||
/>
|
||||
</div>
|
||||
{capacity.map(({ key, value }) => (
|
||||
<div key={key} className={classes.item}>
|
||||
<Typography className={classes.title} noWrap title={key}>
|
||||
{key}
|
||||
</Typography>
|
||||
<Typography variant='body2' noWrap title={value}>
|
||||
{value}
|
||||
</Typography>
|
||||
</div>
|
||||
{capacity.map(({ key, value }) => (
|
||||
<div key={key} className={classes.item}>
|
||||
<Typography className={classes.title} noWrap title={key}>
|
||||
{key}
|
||||
</Typography>
|
||||
<Typography variant='body2' noWrap title={value}>
|
||||
{value}
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
</Paper>
|
||||
</div>
|
||||
))}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ const VmInfoTab = ({ tabProps, ...data }) => {
|
||||
display: 'grid',
|
||||
gap: '1em',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(480px, 1fr))',
|
||||
padding: '1em'
|
||||
padding: '0.8em'
|
||||
}}>
|
||||
{tabProps?.information_panel?.enabled &&
|
||||
<Information {...data} />
|
||||
|
@ -18,69 +18,159 @@ import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Trash } from 'iconoir-react'
|
||||
import { Typography } from '@material-ui/core'
|
||||
import {
|
||||
withStyles,
|
||||
makeStyles,
|
||||
Typography,
|
||||
Accordion,
|
||||
AccordionSummary as MAccordionSummary,
|
||||
AccordionDetails,
|
||||
useMediaQuery,
|
||||
Paper
|
||||
} from '@material-ui/core'
|
||||
|
||||
// import { useVmApi } from 'client/features/One'
|
||||
import { useVmApi } from 'client/features/One'
|
||||
import { Action } from 'client/components/Cards/SelectCard'
|
||||
import { StatusChip } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
import Multiple from 'client/components/Tables/Vms/multiple'
|
||||
|
||||
import { VM_ACTIONS } from 'client/constants'
|
||||
import { T, VM_ACTIONS } from 'client/constants'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const NetworkItem = ({ nic = {}, actions }) => {
|
||||
const classes = rowStyles()
|
||||
const AccordionSummary = withStyles({
|
||||
root: {
|
||||
backgroundColor: 'rgba(0, 0, 0, .03)',
|
||||
borderBottom: '1px solid rgba(0, 0, 0, .125)',
|
||||
marginBottom: -1,
|
||||
minHeight: 56,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, .07)'
|
||||
},
|
||||
'&$expanded': {
|
||||
backgroundColor: 'rgba(0, 0, 0, .07)',
|
||||
minHeight: 56
|
||||
}
|
||||
},
|
||||
content: {
|
||||
overflow: 'hidden',
|
||||
'&$expanded': {
|
||||
margin: '12px 0'
|
||||
}
|
||||
},
|
||||
expanded: {}
|
||||
})(MAccordionSummary)
|
||||
|
||||
const {
|
||||
NIC_ID,
|
||||
NETWORK = '-',
|
||||
BRIDGE,
|
||||
IP,
|
||||
MAC,
|
||||
PCI_ID,
|
||||
ALIAS
|
||||
} = nic
|
||||
const useStyles = makeStyles(({
|
||||
row: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'nowrap'
|
||||
},
|
||||
labels: {
|
||||
display: 'inline-flex',
|
||||
gap: '0.5em',
|
||||
alignItems: 'center'
|
||||
},
|
||||
details: {
|
||||
marginLeft: '1em',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5em'
|
||||
},
|
||||
securityGroups: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5em',
|
||||
padding: '0.8em'
|
||||
}
|
||||
}))
|
||||
|
||||
const NetworkItem = ({ vmId, nic = {}, actions }) => {
|
||||
const classes = useStyles()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm'))
|
||||
const { detachNic, getVm } = useVmApi()
|
||||
|
||||
const { NIC_ID, NETWORK = '-', BRIDGE, IP, MAC, PCI_ID, ALIAS, SECURITY_GROUPS } = nic
|
||||
|
||||
const hasDetails = React.useMemo(() =>
|
||||
!!ALIAS.length || !!SECURITY_GROUPS?.length,
|
||||
[ALIAS.length, SECURITY_GROUPS?.length]
|
||||
)
|
||||
|
||||
const detachAction = () => actions.includes(VM_ACTIONS.DETACH_NIC) && (
|
||||
<Action
|
||||
cy={`${VM_ACTIONS.DETACH_NIC}-${NIC_ID}`}
|
||||
icon={<Trash size={18} />}
|
||||
handleClick={() => undefined}
|
||||
stopPropagation
|
||||
handleClick={async () => {
|
||||
const response = await detachNic(vmId, NIC_ID)
|
||||
|
||||
String(response) === String(vmId) && getVm(vmId)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
const renderLabels = labels => labels
|
||||
?.filter(Boolean)
|
||||
?.map(label => (
|
||||
<StatusChip key={label} text={label} style={{ marginInline: '0.5em' }}/>
|
||||
))
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.main}>
|
||||
<div style={{ borderBottom: ALIAS.length ? '1px solid #c6c6c6' : '' }}>
|
||||
<Typography className={classes.titleText}>
|
||||
<Accordion variant='outlined'>
|
||||
<AccordionSummary>
|
||||
<div className={classes.row}>
|
||||
<Typography noWrap>
|
||||
{`${NIC_ID} | ${NETWORK}`}
|
||||
{renderLabels([IP, MAC, BRIDGE && `BRIDGE - ${BRIDGE}`, PCI_ID])}
|
||||
{detachAction()}
|
||||
</Typography>
|
||||
<span className={classes.labels}>
|
||||
<Multiple
|
||||
limitTags={isMobile ? 1 : 4}
|
||||
tags={[IP, MAC, BRIDGE && `BRIDGE - ${BRIDGE}`, PCI_ID].filter(Boolean)}
|
||||
/>
|
||||
</span>
|
||||
{!isMobile && detachAction()}
|
||||
</div>
|
||||
<div style={{ marginLeft: '1em' }}>
|
||||
</AccordionSummary>
|
||||
{hasDetails && (
|
||||
<AccordionDetails className={classes.details}>
|
||||
{ALIAS?.map(({ NIC_ID, NETWORK = '-', BRIDGE, IP, MAC }) => (
|
||||
<Typography variant='body2' key={NIC_ID}>
|
||||
{`${NIC_ID} | ${NETWORK}`}
|
||||
{renderLabels([IP, MAC, BRIDGE && `BRIDGE - ${BRIDGE}`])}
|
||||
{detachAction()}
|
||||
</Typography>
|
||||
<div key={NIC_ID} className={classes.row}>
|
||||
<Typography noWrap variant='body2'>
|
||||
{`Alias ${NIC_ID} | ${NETWORK}`}
|
||||
</Typography>
|
||||
<span className={classes.labels}>
|
||||
<Multiple
|
||||
limitTags={isMobile ? 1 : 4}
|
||||
tags={[IP, MAC, BRIDGE && `BRIDGE - ${BRIDGE}`].filter(Boolean)}
|
||||
/>
|
||||
</span>
|
||||
{!isMobile && detachAction()}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!!SECURITY_GROUPS?.length && (
|
||||
<Paper variant='outlined' className={classes.securityGroups}>
|
||||
<Typography variant='body1'>{Tr(T.SecurityGroups)}</Typography>
|
||||
|
||||
{SECURITY_GROUPS
|
||||
?.map(({ ID, NAME, PROTOCOL, RULE_TYPE, ICMP_TYPE, RANGE, NETWORK_ID }, idx) => (
|
||||
<div key={`${idx}-${NAME}`} className={classes.row}>
|
||||
<Typography noWrap variant='body2'>
|
||||
{`${ID} | ${NAME}`}
|
||||
</Typography>
|
||||
<span className={classes.labels}>
|
||||
<Multiple
|
||||
limitTags={isMobile ? 2 : 5}
|
||||
tags={[PROTOCOL, RULE_TYPE, RANGE, NETWORK_ID, ICMP_TYPE].filter(Boolean)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</Paper>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
)}
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
|
||||
NetworkItem.propTypes = {
|
||||
nic: PropTypes.object,
|
||||
actions: PropTypes.arrayOf(PropTypes.string)
|
||||
actions: PropTypes.arrayOf(PropTypes.string),
|
||||
vmId: PropTypes.string,
|
||||
nic: PropTypes.object
|
||||
}
|
||||
|
||||
NetworkItem.displayName = 'NetworkItem'
|
||||
|
@ -19,7 +19,7 @@ import PropTypes from 'prop-types'
|
||||
|
||||
import NetworkItem from 'client/components/Tabs/Vm/Network/Item'
|
||||
|
||||
const NetworkList = ({ nics, actions }) => (
|
||||
const NetworkList = ({ vmId, nics, actions }) => (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@ -27,14 +27,15 @@ const NetworkList = ({ nics, actions }) => (
|
||||
paddingBlock: '0.8em'
|
||||
}}>
|
||||
{nics.map((nic, idx) => (
|
||||
<NetworkItem key={idx} nic={nic} actions={actions} />
|
||||
<NetworkItem key={idx} vmId={vmId} nic={nic} actions={actions} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
NetworkList.propTypes = {
|
||||
nics: PropTypes.array,
|
||||
actions: PropTypes.object
|
||||
actions: PropTypes.arrayOf(PropTypes.string),
|
||||
vmId: PropTypes.string,
|
||||
nics: PropTypes.array
|
||||
}
|
||||
|
||||
NetworkList.displayName = 'NetworkList'
|
||||
|
@ -1,50 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as React from 'react'
|
||||
|
||||
import * as VirtualMachine from 'client/models/VirtualMachine'
|
||||
|
||||
const VmNetworkTab = data => {
|
||||
const nics = VirtualMachine.getNics(data, true)
|
||||
// const { nics, alias } = VirtualMachine.splitNicAlias(data)
|
||||
|
||||
console.log(nics)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<p>VM NICS</p>
|
||||
{nics.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-', PCI_ID = '', ALIAS }) => (
|
||||
<div key={NIC_ID}>
|
||||
<p>
|
||||
{`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC} | ${PCI_ID}`}
|
||||
</p>
|
||||
{ALIAS?.map(({ NIC_ID, NETWORK = '-', BRIDGE = '-', IP = '-', MAC = '-' }) => (
|
||||
<p key={NIC_ID} style={{ marginLeft: '1em' }}>
|
||||
{`${NIC_ID} | ${NETWORK} | ${BRIDGE} | ${IP} | ${MAC}`}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
VmNetworkTab.displayName = 'VmNetworkTab'
|
||||
|
||||
export default VmNetworkTab
|
@ -25,7 +25,11 @@ import * as Helper from 'client/models/Helper'
|
||||
const VmNetworkTab = ({ tabProps, ...vm }) => {
|
||||
const { actions = [] } = tabProps
|
||||
|
||||
const nics = VirtualMachine.getNics(vm, { groupAlias: true })
|
||||
const nics = VirtualMachine.getNics(vm, {
|
||||
groupAlias: true,
|
||||
securityGroupsFromTemplate: true
|
||||
})
|
||||
|
||||
const hypervisor = VirtualMachine.getHypervisor(vm)
|
||||
const actionsAvailable = Helper.getActionsAvailable(actions, hypervisor)
|
||||
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
DatabaseSettings, Folder, ModernTv,
|
||||
Trash, SaveActionFloppy, Camera, Expand
|
||||
} from 'iconoir-react'
|
||||
import { Typography } from '@material-ui/core'
|
||||
import { Typography, Paper } from '@material-ui/core'
|
||||
|
||||
// import { useVmApi } from 'client/features/One'
|
||||
import { Action } from 'client/components/Cards/SelectCard'
|
||||
@ -70,7 +70,7 @@ const StorageItem = ({ disk, actions = [] }) => {
|
||||
])].filter(Boolean)
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Paper variant='outlined' className={classes.root}>
|
||||
<div className={classes.main}>
|
||||
<div className={classes.title}>
|
||||
<Typography component='span'>
|
||||
@ -136,7 +136,7 @@ const StorageItem = ({ disk, actions = [] }) => {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,7 @@ import PropTypes from 'prop-types'
|
||||
import StorageItem from 'client/components/Tabs/Vm/Storage/Item'
|
||||
|
||||
const StorageList = ({ disks, actions }) => (
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gap: '1em',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(49%, 1fr))',
|
||||
paddingBlock: '0.8em'
|
||||
}}>
|
||||
<div style={{ display: 'grid', gap: '1em', paddingBlock: '0.8em' }}>
|
||||
{disks.map((disk, idx) => (
|
||||
<StorageItem
|
||||
key={idx}
|
||||
@ -38,7 +33,7 @@ const StorageList = ({ disks, actions }) => (
|
||||
|
||||
StorageList.propTypes = {
|
||||
disks: PropTypes.array,
|
||||
actions: PropTypes.object
|
||||
actions: PropTypes.arrayOf(PropTypes.string)
|
||||
}
|
||||
|
||||
StorageList.displayName = 'StorageList'
|
||||
|
@ -20,6 +20,7 @@ module.exports = {
|
||||
Next: 'Next',
|
||||
SortBy: 'Sort by',
|
||||
Filter: 'Filter',
|
||||
All: 'All',
|
||||
|
||||
/* actions */
|
||||
Accept: 'Accept',
|
||||
@ -123,6 +124,8 @@ module.exports = {
|
||||
VirtualNetworks: 'Virtual networks',
|
||||
NetworkTopology: 'Network topology',
|
||||
NetworksTopologies: 'Networks topologies',
|
||||
SecurityGroup: 'Security group',
|
||||
SecurityGroups: 'Security groups',
|
||||
|
||||
/* sections - storage */
|
||||
Datastore: 'Datastore',
|
||||
@ -188,5 +191,14 @@ module.exports = {
|
||||
/* ownership */
|
||||
Ownership: 'Ownership',
|
||||
Owner: 'Owner',
|
||||
Other: 'Other'
|
||||
Other: 'Other',
|
||||
|
||||
/* security group schema */
|
||||
TCP: 'TCP',
|
||||
UDP: 'UDP',
|
||||
ICMP: 'ICMP',
|
||||
ICMPV6: 'ICMPv6',
|
||||
IPSEC: 'IPsec',
|
||||
Outbound: 'Outbound',
|
||||
Inbound: 'Inbound'
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ export const eventApi = createAsyncThunk(
|
||||
*/
|
||||
export const eventUpdateResourceState = createAsyncThunk(
|
||||
'socket/event-state',
|
||||
({ data } = {}) => {
|
||||
(data = {}) => {
|
||||
const { name, value } = getResourceFromEventState(data)
|
||||
|
||||
return { type: name, data: value }
|
||||
},
|
||||
{
|
||||
condition: ({ data } = {}) => {
|
||||
condition: (data = {}) => {
|
||||
const { name, value } = getResourceFromEventState(data)
|
||||
|
||||
return (
|
||||
|
@ -41,3 +41,4 @@ export const terminateVm = createAction(
|
||||
)
|
||||
|
||||
export const changePermissions = createAction('vm/chmod', vmService.changePermissions)
|
||||
export const detachNic = createAction('vm/detach/nic', vmService.detachNic)
|
||||
|
@ -36,6 +36,8 @@ export const useVmApi = () => {
|
||||
getVm: id => unwrapDispatch(actions.getVm({ id })),
|
||||
getVms: options => unwrapDispatch(actions.getVms(options)),
|
||||
terminateVm: id => unwrapDispatch(actions.terminateVm({ id })),
|
||||
changePermissions: (id, data) => unwrapDispatch(actions.changePermissions({ id, data }))
|
||||
changePermissions: (id, permissions) =>
|
||||
unwrapDispatch(actions.changePermissions({ id, permissions })),
|
||||
detachNic: (id, nic) => unwrapDispatch(actions.detachNic({ id, nic }))
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ export const vmService = ({
|
||||
* Changes the permission bits of a virtual machine.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string} params.id - Virtual machine id
|
||||
* @param {string|number} params.id - Virtual machine id
|
||||
* @param {{
|
||||
* ownerUse: number,
|
||||
* ownerManage: number,
|
||||
@ -113,19 +113,40 @@ export const vmService = ({
|
||||
* otherUse: number,
|
||||
* otherManage: number,
|
||||
* otherAdmin: number
|
||||
* }} params.data - Permissions data
|
||||
* @returns {Response} Response
|
||||
* }} params.permissions - Permissions data
|
||||
* @returns {number} Virtual machine id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
changePermissions: async ({ id, data }) => {
|
||||
changePermissions: async ({ id, permissions }) => {
|
||||
const name = Actions.VM_CHMOD
|
||||
const command = { name, ...Commands[name] }
|
||||
const config = requestConfig({ id, data }, command)
|
||||
const config = requestConfig({ id, ...permissions }, command)
|
||||
|
||||
const res = await RestClient.request(config)
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
|
||||
|
||||
return res
|
||||
return res?.data
|
||||
},
|
||||
|
||||
/**
|
||||
* Detaches a network interface from a virtual machine.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Virtual machine id
|
||||
* @param {string|number} params.nic - NIC id
|
||||
* @returns {number} Virtual machine id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
detachNic: async ({ id, nic }) => {
|
||||
const name = Actions.VM_NIC_DETACH
|
||||
const command = { name, ...Commands[name] }
|
||||
const config = requestConfig({ id, nic }, command)
|
||||
|
||||
const res = await RestClient.request(config)
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
|
||||
|
||||
return res?.data
|
||||
}
|
||||
})
|
||||
|
@ -96,7 +96,8 @@ const useFetch = (request, socket) => {
|
||||
useEffect(() => {
|
||||
isFetched && socket?.connect({
|
||||
dataFromFetch: state.data,
|
||||
callback: socketData => dispatch({ type: ACTIONS.SUCCESS, payload: socketData })
|
||||
callback: socketData =>
|
||||
socketData && dispatch({ type: ACTIONS.SUCCESS, payload: socketData })
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
@ -64,7 +64,7 @@ const useSocket = () => {
|
||||
dispatch(updateResourceFromFetch({ data: dataFromFetch, resource }))
|
||||
})
|
||||
|
||||
socket.on(SOCKETS.HOOKS, data => {
|
||||
socket.on(SOCKETS.HOOKS, ({ data } = {}) => {
|
||||
// update the list on redux state
|
||||
dispatch(eventUpdateResourceState(data))
|
||||
// return data from event
|
||||
|
160
src/fireedge/src/client/models/SecurityGroup.js
Normal file
160
src/fireedge/src/client/models/SecurityGroup.js
Normal file
@ -0,0 +1,160 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, 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 { T } from 'client/constants'
|
||||
|
||||
/**
|
||||
* ICMP Codes for each ICMP type as in:
|
||||
* http://www.iana.org/assignments/icmp-parameters/
|
||||
*
|
||||
* @enum {string} Security group ICMP type
|
||||
*/
|
||||
const ICMP_STRING = {
|
||||
'': 'All',
|
||||
0: '0: Echo Reply',
|
||||
3: '3: Destination Unreachable',
|
||||
4: '4: Source Quench', // Deprecated
|
||||
5: '5: Redirect',
|
||||
6: '6: Alternate Host Address', // Deprecated
|
||||
8: '8: Echo',
|
||||
9: '9: Router Advertisement',
|
||||
10: '10: Router Selection',
|
||||
11: '11: Time Exceeded',
|
||||
12: '12: Parameter Problem',
|
||||
13: '13: Timestamp',
|
||||
14: '14: Timestamp Reply',
|
||||
15: '15: Information Request', // Deprecated
|
||||
16: '16: Information Reply', // Deprecated
|
||||
17: '17: Address Mask Request', // Deprecated
|
||||
18: '18: Address Mask Reply', // Deprecated
|
||||
30: '30: Traceroute', // Deprecated
|
||||
31: '31: Datagram Conversion Error', // Deprecated
|
||||
32: '32: Mobile Host Redirect', // Deprecated
|
||||
33: '33: IPv6 Where-Are-You', // Deprecated
|
||||
34: '34: IPv6 I-Am-Here', // Deprecated
|
||||
35: '35: Mobile Registration Request', // Deprecated
|
||||
36: '36: Mobile Registration Reply', // Deprecated
|
||||
37: '37: Domain Name Request', // Deprecated
|
||||
38: '38: Domain Name Reply', // Deprecated
|
||||
39: '39: SKIP', // Deprecated
|
||||
40: '40: Photuris',
|
||||
41: '41: ICMP messages utilized by experimental mobility protocols such as Seamoby',
|
||||
253: '253: RFC3692-style Experiment 1',
|
||||
254: '254: RFC3692-style Experiment 2'
|
||||
}
|
||||
|
||||
/**
|
||||
* ICMPv6 Codes for each ICMPv6 type as in:
|
||||
* http://www.iana.org/assignments/icmpv6-parameters/
|
||||
*
|
||||
* @enum {string} Security group ICMP v6 type
|
||||
*/
|
||||
const ICMP_V6_STRING = {
|
||||
'': 'All',
|
||||
1: '1: Destination Unreachable',
|
||||
2: '2/0: Packet too big',
|
||||
3: '3: Time exceeded',
|
||||
4: '4: Parameter problem',
|
||||
128: '128/0: Echo request',
|
||||
129: '129/0: Echo reply'
|
||||
}
|
||||
|
||||
/** @enum {string} Security group protocol */
|
||||
const PROTOCOL_STRING = {
|
||||
TCP: T.TCP,
|
||||
UDP: T.UDP,
|
||||
ICMP: T.ICMP,
|
||||
ICMPV6: T.ICMPV6,
|
||||
IPSEC: T.IPSEC,
|
||||
ALL: T.All
|
||||
}
|
||||
|
||||
/** @enum {string} Security group rule type */
|
||||
const RULE_TYPE_STRING = {
|
||||
OUTBOUND: T.Outbound,
|
||||
INBOUND: T.Inbound
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a security group attributes into a readable format.
|
||||
*
|
||||
* @param {object} securityGroup - Security group
|
||||
* @param {number|string} securityGroup.SECURITY_GROUP_ID - Id
|
||||
* @param {string} securityGroup.SECURITY_GROUP_NAME - Name
|
||||
* @param {string} securityGroup.PROTOCOL - Protocol
|
||||
* @param {string} securityGroup.RULE_TYPE - Rule type
|
||||
* @param {number|string} securityGroup.ICMP_TYPE - ICMP type
|
||||
* @param {number|string} securityGroup.ICMPv6_TYPE - ICMP v6 type
|
||||
* @param {number|string} securityGroup.RANGE - Range
|
||||
* @param {number|string} securityGroup.NETWORK_ID - Network id
|
||||
* @param {number|string} securityGroup.SIZE - Network size
|
||||
* @param {string} securityGroup.IP - Network IP
|
||||
* @param {string} securityGroup.MAC - Network MAC
|
||||
* @returns {{
|
||||
* SECURITY_GROUP_ID: number|string,
|
||||
* SECURITY_GROUP_NAME: string,
|
||||
* PROTOCOL: PROTOCOL_STRING,
|
||||
* RULE_TYPE: RULE_TYPE_STRING,
|
||||
* ICMP_TYPE: ICMP_STRING,
|
||||
* ICMPv6_TYPE: ICMP_V6_STRING,
|
||||
* RANGE: string,
|
||||
* NETWORK_ID: number|string,
|
||||
* SIZE: number|string,
|
||||
* IP: string,
|
||||
* MAC: string
|
||||
* }} Readable attributes
|
||||
*/
|
||||
export const prettySecurityGroup = ({
|
||||
SECURITY_GROUP_ID: ID,
|
||||
SECURITY_GROUP_NAME: NAME,
|
||||
PROTOCOL: protocol,
|
||||
RULE_TYPE: ruleType,
|
||||
ICMP_TYPE: icmpType,
|
||||
ICMPv6_TYPE: icmpv6Type,
|
||||
RANGE: range,
|
||||
NETWORK_ID,
|
||||
SIZE,
|
||||
IP,
|
||||
MAC
|
||||
}) => ({
|
||||
ID,
|
||||
NAME,
|
||||
PROTOCOL: PROTOCOL_STRING[String(protocol).toUpperCase()],
|
||||
RULE_TYPE: RULE_TYPE_STRING[String(ruleType).toUpperCase()],
|
||||
ICMP_TYPE: ICMP_STRING[+icmpType] ?? '',
|
||||
ICMPv6_TYPE: ICMP_V6_STRING[+icmpv6Type] ?? '',
|
||||
RANGE: range || T.All,
|
||||
NETWORK_ID,
|
||||
SIZE,
|
||||
IP,
|
||||
MAC
|
||||
})
|
||||
|
||||
/**
|
||||
* Selects security groups from OpenNebula resource.
|
||||
*
|
||||
* @param {object} resource - OpenNebula resource
|
||||
* @param {string|string[]} securityGroups - List of security group
|
||||
* @returns {object[]} List of security groups
|
||||
*/
|
||||
export const getSecurityGroupsFromResource = (resource, securityGroups) => {
|
||||
const rules = [resource?.TEMPLATE?.SECURITY_GROUP_RULE ?? []].flat()
|
||||
|
||||
const groups = Array.isArray(securityGroups)
|
||||
? securityGroups
|
||||
: securityGroups.split(',')
|
||||
|
||||
return rules.filter(({ SECURITY_GROUP_ID }) => groups.includes?.(SECURITY_GROUP_ID))
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
import { STATES, VM_STATES, VM_LCM_STATES, StateInfo } from 'client/constants'
|
||||
import { getSecurityGroupsFromResource, prettySecurityGroup } from 'client/models/SecurityGroup'
|
||||
|
||||
/* const EXTERNAL_IP_ATTRS = [
|
||||
'GUEST_IP',
|
||||
@ -124,11 +125,14 @@ export const getDisks = vm => {
|
||||
/**
|
||||
* @param {object} vm - Virtual machine
|
||||
* @param {object} [options] - Options
|
||||
* @param {boolean} [options.groupAlias] - Map ALIAS_IDS attribute with NIC_ALIAS
|
||||
* @returns {Array} List of nics from resource
|
||||
* @param {boolean} [options.groupAlias]
|
||||
* - Create ALIAS attribute with result to mapping NIC_ALIAS and ALIAS_IDS
|
||||
* @param {boolean} [options.securityGroupsFromTemplate]
|
||||
* - Create SECURITY_GROUPS attribute with rules from TEMPLATE.SECURITY_GROUP_RULE
|
||||
* @returns {object[]} List of nics from resource
|
||||
*/
|
||||
export const getNics = (vm, options = {}) => {
|
||||
const { groupAlias = false } = options
|
||||
const { groupAlias = false, securityGroupsFromTemplate = false } = options
|
||||
const { TEMPLATE = {}, MONITORING = {} } = vm ?? {}
|
||||
|
||||
const { NIC = [], NIC_ALIAS = [], PCI = [] } = TEMPLATE
|
||||
@ -138,14 +142,29 @@ export const getNics = (vm, options = {}) => {
|
||||
.filter(Boolean)
|
||||
.map(ip => ({ NIC_ID: '-', IP: ip, NETWORK: 'Additional IP', BRIDGE: '-' }))
|
||||
|
||||
const nics = [NIC, PCI, extraIps].flat().filter(Boolean)
|
||||
let nics = [NIC, NIC_ALIAS, PCI, extraIps].flat().filter(Boolean)
|
||||
|
||||
return groupAlias
|
||||
? nics.map(({ ALIAS_IDS, ...nic }) => ({
|
||||
if (groupAlias) {
|
||||
nics = nics
|
||||
.filter(({ PARENT }) => PARENT === undefined)
|
||||
.map(({ ALIAS_IDS, ...nic }) => ({
|
||||
...nic,
|
||||
ALIAS: [NIC_ALIAS]
|
||||
.flat()
|
||||
.filter(({ NIC_ID }) => ALIAS_IDS?.split(',')?.includes?.(NIC_ID))
|
||||
}))
|
||||
}
|
||||
|
||||
if (securityGroupsFromTemplate) {
|
||||
nics = nics.map(({ SECURITY_GROUPS, ...nic }) => ({
|
||||
...nic,
|
||||
ALIAS: NIC_ALIAS?.filter(({ NIC_ID }) => ALIAS_IDS?.includes(NIC_ID))
|
||||
SECURITY_GROUPS:
|
||||
getSecurityGroupsFromResource(vm, SECURITY_GROUPS)
|
||||
?.map(prettySecurityGroup)
|
||||
}))
|
||||
: nics.concat(NIC_ALIAS)
|
||||
}
|
||||
|
||||
return nics
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user