1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00

F #3951: Add configure action to provision (#668)

This commit is contained in:
Sergio Betanzos 2021-01-19 10:15:57 +01:00 committed by GitHub
parent 5e09101b92
commit 8b03aae0fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 97 additions and 38 deletions

View File

@ -44,7 +44,10 @@ const DatastoreCard = memo(
/>
)
},
(prev, next) => prev.isSelected === next.isSelected
(prev, next) => (
prev.isSelected === next.isSelected &&
prev.value?.STATE === next.value?.STATE
)
)
DatastoreCard.propTypes = {

View File

@ -26,7 +26,7 @@ const HostCard = memo(
stylesProps={{ minHeight: 160 }}
cardProps={{ className: classes.card, elevation: 2 }}
icon={
<StatusBadge stateColor={state.color}>
<StatusBadge title={state?.name} stateColor={state.color}>
<HostIcon />
</StatusBadge>
}
@ -46,7 +46,10 @@ const HostCard = memo(
/>
)
},
(prev, next) => prev.isSelected === next.isSelected
(prev, next) => (
prev.isSelected === next.isSelected &&
prev.value?.STATE === next.value?.STATE
)
)
HostCard.propTypes = {

View File

@ -49,7 +49,7 @@ const ProvisionCard = memo(
isProvider ? (
<ProviderIcon />
) : (
<StatusBadge stateColor={stateInfo?.color}>
<StatusBadge title={stateInfo?.name} stateColor={stateInfo?.color}>
<ProvisionIcon />
</StatusBadge>
)
@ -62,7 +62,12 @@ const ProvisionCard = memo(
}}
/>
)
}, (prev, next) => prev.isSelected === next.isSelected
}, (prev, next) => (
prev.isSelected === next.isSelected &&
!prev.isProvider &&
!next.isProvider &&
prev.value?.BODY?.state === next.value?.BODY?.state
)
)
ProvisionCard.propTypes = {

View File

@ -96,7 +96,7 @@ ansiHTML.setColors = function (colors) {
var _finalColors = {}
for (var key in _defColors) {
var hex = colors.hasOwnProperty(key) ? colors[key] : null
var hex = Object.prototype.hasOwnProperty.call(colors, key) ? colors[key] : null
if (!hex) {
_finalColors[key] = _defColors[key]
continue

View File

@ -4,6 +4,7 @@ import clsx from 'clsx'
import { makeStyles } from '@material-ui/core'
import ChevronRightIcon from '@material-ui/icons/ChevronRight'
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
import { DEBUG_LEVEL } from 'client/constants'
import AnsiHtml from 'client/components/DebugLog/ansiHtml'
@ -16,7 +17,7 @@ const useStyles = makeStyles(theme => ({
alignItems: 'center',
marginBottom: '0.3em',
padding: '0.5em 0',
cursor: ({ isCollapsed }) => isCollapsed ? 'pointer' : 'default',
cursor: ({ isMoreThanMaxChars }) => isMoreThanMaxChars ? 'pointer' : 'default',
fontFamily: 'monospace',
'&:hover': {
background: '#333537'
@ -43,29 +44,35 @@ const useStyles = makeStyles(theme => ({
// --------------------------------------------
const Message = memo(({ timestamp, severity, message }) => {
const [isCollapsed, setCollapse] = useState(() => message?.length >= MAX_CHARS)
const classes = useStyles({ isCollapsed })
const sanitize = AnsiHtml(message)
const isMoreThanMaxChars = message?.length >= MAX_CHARS
const [isCollapsed, setCollapse] = useState(() => isMoreThanMaxChars)
const classes = useStyles({ isMoreThanMaxChars })
return (
<div
className={clsx(classes.root, classes[severity])}
onClick={() => setCollapse(false)}
onClick={() => setCollapse(prev => !prev)}
>
<div className={classes.arrow}>
{isCollapsed && <ChevronRightIcon fontSize='small' />}
{isMoreThanMaxChars && (isCollapsed ? (
<ChevronRightIcon fontSize='small' />
) : (
<ExpandMoreIcon fontSize='small' />
))}
</div>
<div className={classes.time}>{timestamp}</div>
<div className={classes.message}>
{isCollapsed ? `${sanitize.slice(0, MAX_CHARS)}...` : sanitize}
</div>
{(isCollapsed && isMoreThanMaxChars) ? (
<div className={classes.message}>{`${message?.slice(0, MAX_CHARS)}`}</div>
) : (
<div className={classes.message} dangerouslySetInnerHTML={{ __html: AnsiHtml(message) }} />
)}
</div>
)
})
Message.propTypes = {
timestamp: PropTypes.string,
severity: PropTypes.oneOf([Object.keys(DEBUG_LEVEL)]),
severity: PropTypes.oneOf(Object.keys(DEBUG_LEVEL)),
message: PropTypes.string
}

View File

@ -32,7 +32,7 @@ const useStyles = makeStyles(theme => ({
}
}))
const StatusBadge = memo(({ stateColor, children }) => {
const StatusBadge = memo(({ stateColor, children, ...props }) => {
const classes = useStyles({ stateColor })
return (
@ -41,6 +41,7 @@ const StatusBadge = memo(({ stateColor, children }) => {
classes={{ badge: classes.badge }}
overlap="circle"
variant="dot"
{...props}
>
{children}
</Badge>

View File

@ -2,6 +2,7 @@ import React, { useState, useEffect, createElement } from 'react'
import { useHistory } from 'react-router-dom'
import { Container, Box } from '@material-ui/core'
import EditIcon from '@material-ui/icons/Settings'
import DeleteIcon from '@material-ui/icons/Delete'
import { PATH } from 'client/router/provision'
@ -18,7 +19,13 @@ function Provisions () {
const history = useHistory()
const [showDialog, setShowDialog] = useState(false)
const { provisions, getProvisions, getProvision, deleteProvision } = useProvision()
const {
provisions,
getProvisions,
getProvision,
configureProvision,
deleteProvision
} = useProvision()
const { error, fetchRequest, loading, reloading } = useFetch(getProvisions)
const { result, handleChange } = useSearch({
@ -56,19 +63,26 @@ function Provisions () {
subheader: `#${ID}`,
content: DialogInfo
}),
actions: [{
handleClick: () => setShowDialog({
id: ID,
title: `DELETE provision - #${ID} - ${NAME}`,
handleAccept: () => {
deleteProvision({ id: ID })
setShowDialog(false)
},
content: DialogInfo
}),
icon: <DeleteIcon color='error' />,
cy: `provision-delete-${ID}`
}]
actions: [
{
handleClick: () => configureProvision({ id: ID }),
icon: <EditIcon />,
cy: `provision-configure-${ID}`
},
{
handleClick: () => setShowDialog({
id: ID,
title: `DELETE provision - #${ID} - ${NAME}`,
handleAccept: () => {
deleteProvision({ id: ID })
setShowDialog(false)
},
content: DialogInfo
}),
icon: <DeleteIcon color='error' />,
cy: `provision-delete-${ID}`
}
]
})}
breakpoints={{ xs: 12, sm: 6, md: 4 }}
/>

View File

@ -142,6 +142,18 @@ export default function useProvision () {
[dispatch]
)
const configureProvision = useCallback(
({ id }) =>
serviceProvision
.configureProvision({ id })
.then(doc => {
dispatch(enqueueSuccess(`Provision configuring - ID: ${id}`))
return doc
})
.catch(err => dispatch(enqueueError(err ?? 'Error CONFIGURE provision')))
, [dispatch]
)
const deleteProvision = useCallback(
({ id }) =>
serviceProvision
@ -227,6 +239,7 @@ export default function useProvision () {
getProvision,
getProvisions,
createProvision,
configureProvision,
deleteProvision,
getProvisionLog,

View File

@ -62,6 +62,19 @@ export const createProvision = ({ data = {} }) =>
return res?.data ?? {}
})
export const configureProvision = ({ id }) =>
requestData(`/api/${PROVISION}/configure/${id}`, {
method: PUT,
error: err => err?.message
}).then(res => {
if (!res?.id || res?.id !== httpCodes.ok.id) {
if (res?.id === httpCodes.accepted.id) return res
throw res
}
return res?.data ?? {}
})
export const deleteProvision = ({ id }) =>
requestData(`/api/${PROVISION}/delete/${id}`, {
method: DELETE,

View File

@ -105,13 +105,6 @@ const routes = {
resource: { from: fromData.postBody, front: true }
}
},
configure: {
action: configureProvision,
params: {
resource: { from: fromData.resource, name: 'method' }
},
websocket: true
},
host: {
poweroff: {
action: hostCommand,
@ -210,6 +203,13 @@ const routes = {
}
},
[PUT]: {
configure: {
action: configureProvision,
params: {
id: { from: fromData.resource, name: 'id' }
},
websocket: true
},
host: {
action: configureHost,
params: {