mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
(cherry picked from commit 9733184d3301f7795d84941b5078bbaf27191602)
This commit is contained in:
parent
9e73cd5cbb
commit
1f21b0040b
@ -69,7 +69,7 @@ const DatastoreCard = memo(
|
||||
<Typography title={NAME} noWrap component="span">
|
||||
{NAME}
|
||||
</Typography>
|
||||
<StatusChip text={type.name} />
|
||||
<StatusChip text={type} />
|
||||
</span>
|
||||
}
|
||||
subheader={`#${ID}`}
|
||||
|
@ -13,24 +13,30 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { object, boolean } from 'yup'
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
const CLEANUP = {
|
||||
name: 'cleanup',
|
||||
label: 'Cleanup',
|
||||
label: T.Cleanup,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
tooltip: `
|
||||
Force to terminate VMs running on provisioned Hosts
|
||||
and delete all images in the datastores.`,
|
||||
validation: yup
|
||||
.boolean()
|
||||
tooltip: T.CleanupConcept,
|
||||
validation: boolean()
|
||||
.notRequired()
|
||||
.default(() => false),
|
||||
}
|
||||
|
||||
export const FIELDS = [CLEANUP]
|
||||
const FORCE = {
|
||||
name: 'force',
|
||||
label: T.Force,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
tooltip: T.ForceConcept,
|
||||
validation: boolean()
|
||||
.notRequired()
|
||||
.default(() => false),
|
||||
}
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS))
|
||||
export const FIELDS = [CLEANUP, FORCE]
|
||||
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
||||
|
@ -228,6 +228,12 @@ module.exports = {
|
||||
ProviderTemplate: 'Provider template',
|
||||
ProvisionTemplate: 'Provision template',
|
||||
ConfigureInputs: 'Configure inputs',
|
||||
AddIP: 'Add IP',
|
||||
AddHost: 'Add Host',
|
||||
Cleanup: 'Cleanup',
|
||||
CleanupConcept: 'Delete all vms and images first, then delete the resources',
|
||||
Force: 'Force',
|
||||
ForceConcept: 'Force configure to execute',
|
||||
|
||||
/* sections */
|
||||
Dashboard: 'Dashboard',
|
||||
|
@ -13,25 +13,42 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo, useEffect } from 'react'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Trash as DeleteIcon, Settings as ConfigureIcon } from 'iconoir-react'
|
||||
import {
|
||||
Trash as DeleteIcon,
|
||||
Settings as ConfigureIcon,
|
||||
AddCircledOutline,
|
||||
} from 'iconoir-react'
|
||||
import { Stack, TextField } from '@mui/material'
|
||||
|
||||
import { useFetchAll } from 'client/hooks'
|
||||
import { useFetch, useFetchAll } from 'client/hooks'
|
||||
import { useHostApi, useProvisionApi } from 'client/features/One'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { ListCards } from 'client/components/List'
|
||||
import { HostCard } from 'client/components/Cards'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Hosts = memo(
|
||||
({ hidden, data, reloading, refetchProvision, disableAllActions }) => {
|
||||
const [amount, setAmount] = useState(() => 1)
|
||||
const { hosts = [] } = data?.TEMPLATE?.BODY?.provision?.infrastructure
|
||||
|
||||
const { enqueueSuccess, enqueueInfo } = useGeneralApi()
|
||||
const { configureHost, deleteHost } = useProvisionApi()
|
||||
const { configureHost, deleteHost, addHost } = useProvisionApi()
|
||||
const { getHost } = useHostApi()
|
||||
|
||||
const { fetchRequest, loading: loadingAddHost } = useFetch(
|
||||
async (payload) => {
|
||||
await addHost(data?.ID, payload)
|
||||
await refetchProvision()
|
||||
enqueueSuccess(`Host added ${amount}x`)
|
||||
}
|
||||
)
|
||||
|
||||
const { data: list, fetchRequestAll, loading } = useFetchAll()
|
||||
const fetchHosts = () =>
|
||||
fetchRequestAll(hosts?.map(({ id }) => getHost(id)))
|
||||
@ -45,35 +62,59 @@ const Hosts = memo(
|
||||
}, [reloading])
|
||||
|
||||
return (
|
||||
<ListCards
|
||||
list={list}
|
||||
isLoading={!list && loading}
|
||||
CardComponent={HostCard}
|
||||
cardsProps={({ value: { ID } }) =>
|
||||
!disableAllActions && {
|
||||
actions: [
|
||||
{
|
||||
handleClick: () =>
|
||||
configureHost(ID)
|
||||
.then(() => enqueueInfo(`Configuring host - ID: ${ID}`))
|
||||
.then(refetchProvision),
|
||||
icon: <ConfigureIcon />,
|
||||
cy: `provision-host-configure-${ID}`,
|
||||
},
|
||||
{
|
||||
handleClick: () =>
|
||||
deleteHost(ID)
|
||||
.then(refetchProvision)
|
||||
.then(() => enqueueSuccess(`Host deleted - ID: ${ID}`)),
|
||||
icon: <DeleteIcon color="error" />,
|
||||
cy: `provision-host-delete-${ID}`,
|
||||
},
|
||||
],
|
||||
<>
|
||||
<Stack direction="row" mb="0.5em">
|
||||
<TextField
|
||||
type="number"
|
||||
inputProps={{ 'data-cy': 'amount' }}
|
||||
onChange={(event) => {
|
||||
const newAmount = event.target.value
|
||||
;+newAmount > 0 && setAmount(newAmount)
|
||||
}}
|
||||
value={amount}
|
||||
/>
|
||||
<Stack alignSelf="center">
|
||||
<SubmitButton
|
||||
data-cy="add-host"
|
||||
color="secondary"
|
||||
sx={{ ml: 1, display: 'flex', alignItems: 'flex-start' }}
|
||||
endicon={<AddCircledOutline />}
|
||||
label={<Translate word={T.AddHost} />}
|
||||
isSubmitting={loadingAddHost}
|
||||
onClick={() => fetchRequest(amount)}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<ListCards
|
||||
list={list}
|
||||
isLoading={!list && loading}
|
||||
CardComponent={HostCard}
|
||||
cardsProps={({ value: { ID } }) =>
|
||||
!disableAllActions && {
|
||||
actions: [
|
||||
{
|
||||
handleClick: () =>
|
||||
configureHost(ID)
|
||||
.then(() => enqueueInfo(`Configuring host - ID: ${ID}`))
|
||||
.then(refetchProvision),
|
||||
icon: <ConfigureIcon />,
|
||||
cy: `provision-host-configure-${ID}`,
|
||||
},
|
||||
{
|
||||
handleClick: () =>
|
||||
deleteHost(ID)
|
||||
.then(refetchProvision)
|
||||
.then(() => enqueueSuccess(`Host deleted - ID: ${ID}`)),
|
||||
icon: <DeleteIcon color="error" />,
|
||||
cy: `provision-host-delete-${ID}`,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
displayEmpty
|
||||
breakpoints={{ xs: 12, md: 6 }}
|
||||
/>
|
||||
displayEmpty
|
||||
breakpoints={{ xs: 12, md: 6 }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
(prev, next) =>
|
||||
|
@ -13,25 +13,38 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo, useEffect } from 'react'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Trash as DeleteIcon } from 'iconoir-react'
|
||||
import { Trash as DeleteIcon, AddCircledOutline } from 'iconoir-react'
|
||||
import { Stack, TextField } from '@mui/material'
|
||||
|
||||
import { useFetchAll } from 'client/hooks'
|
||||
import { useFetch, useFetchAll } from 'client/hooks'
|
||||
import { useVNetworkApi, useProvisionApi } from 'client/features/One'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { ListCards } from 'client/components/List'
|
||||
import { NetworkCard } from 'client/components/Cards'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Networks = memo(
|
||||
({ hidden, data, reloading, refetchProvision, disableAllActions }) => {
|
||||
const [amount, setAmount] = useState(() => 1)
|
||||
const { networks = [] } = data?.TEMPLATE?.BODY?.provision?.infrastructure
|
||||
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
const { deleteVNetwork } = useProvisionApi()
|
||||
const { deleteVNetwork, addIp } = useProvisionApi()
|
||||
const { getVNetwork } = useVNetworkApi()
|
||||
|
||||
const { fetchRequest, loading: loadingAddIp } = useFetch(
|
||||
async (payload) => {
|
||||
await addIp(data?.ID, payload)
|
||||
await refetchProvision()
|
||||
enqueueSuccess(`IP added ${amount}x`)
|
||||
}
|
||||
)
|
||||
|
||||
const { data: list, fetchRequestAll, loading } = useFetchAll()
|
||||
const fetchVNetworks = () =>
|
||||
fetchRequestAll(networks?.map(({ id }) => getVNetwork(id)))
|
||||
@ -45,27 +58,53 @@ const Networks = memo(
|
||||
}, [reloading])
|
||||
|
||||
return (
|
||||
<ListCards
|
||||
list={list}
|
||||
isLoading={!list && loading}
|
||||
CardComponent={NetworkCard}
|
||||
cardsProps={({ value: { ID } }) =>
|
||||
!disableAllActions && {
|
||||
actions: [
|
||||
{
|
||||
handleClick: () =>
|
||||
deleteVNetwork(ID)
|
||||
.then(refetchProvision)
|
||||
.then(() => enqueueSuccess(`VNetwork deleted - ID: ${ID}`)),
|
||||
icon: <DeleteIcon color="error" />,
|
||||
cy: `provision-vnet-delete-${ID}`,
|
||||
},
|
||||
],
|
||||
<>
|
||||
<Stack direction="row" mb="0.5em">
|
||||
<TextField
|
||||
type="number"
|
||||
inputProps={{ 'data-cy': 'amount' }}
|
||||
onChange={(event) => {
|
||||
const newAmount = event.target.value
|
||||
;+newAmount > 0 && setAmount(newAmount)
|
||||
}}
|
||||
value={amount}
|
||||
/>
|
||||
<Stack alignSelf="center">
|
||||
<SubmitButton
|
||||
data-cy="add-ip"
|
||||
color="secondary"
|
||||
endicon={<AddCircledOutline />}
|
||||
label={<Translate word={T.AddIP} />}
|
||||
sx={{ ml: 1, display: 'flex', alignItems: 'flex-start' }}
|
||||
isSubmitting={loadingAddIp}
|
||||
onClick={() => fetchRequest(amount)}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<ListCards
|
||||
list={list}
|
||||
isLoading={!list && loading}
|
||||
CardComponent={NetworkCard}
|
||||
cardsProps={({ value: { ID } }) =>
|
||||
!disableAllActions && {
|
||||
actions: [
|
||||
{
|
||||
handleClick: () =>
|
||||
deleteVNetwork(ID)
|
||||
.then(refetchProvision)
|
||||
.then(() =>
|
||||
enqueueSuccess(`VNetwork deleted - ID: ${ID}`)
|
||||
),
|
||||
icon: <DeleteIcon color="error" />,
|
||||
cy: `provision-vnet-delete-${ID}`,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
displayEmpty
|
||||
breakpoints={{ xs: 12, md: 6 }}
|
||||
/>
|
||||
displayEmpty
|
||||
breakpoints={{ xs: 12, md: 6 }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
(prev, next) =>
|
||||
|
@ -72,3 +72,8 @@ export const configureHost = createAction(
|
||||
'provision/host/configure',
|
||||
provisionService.configureHost
|
||||
)
|
||||
export const addHost = createAction(
|
||||
'provision/host/add',
|
||||
provisionService.addHost
|
||||
)
|
||||
export const addIp = createAction('provision/ip/add', provisionService.addIp)
|
||||
|
@ -55,5 +55,7 @@ export const useProvisionApi = () => {
|
||||
deleteVNetwork: (id) => unwrapDispatch(actions.deleteVNetwork({ id })),
|
||||
deleteHost: (id) => unwrapDispatch(actions.deleteHost({ id })),
|
||||
configureHost: (id) => unwrapDispatch(actions.configureHost({ id })),
|
||||
addHost: (id, amount) => unwrapDispatch(actions.addHost({ id, amount })),
|
||||
addIp: (id, amount) => unwrapDispatch(actions.addIp({ id, amount })),
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,8 @@ export const provisionService = {
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {object} params.id - Provider id
|
||||
* @param {object} params.cleanup
|
||||
* @param {boolean} params.force - Force configure to execute
|
||||
* @param {boolean} params.cleanup
|
||||
* - If `true`, force to terminate VMs running
|
||||
* on provisioned Hosts and delete all images in the datastores
|
||||
* @returns {object} Object of document deleted
|
||||
@ -261,4 +262,52 @@ export const provisionService = {
|
||||
|
||||
return res?.data ?? {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Provisions and configures a new host or amount of hosts.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {object} params.id - Provision id
|
||||
* @param {object} params.amount - Amount of hosts to add to the provision
|
||||
* @returns {object} Object of document updated
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
addHost: async ({ id, amount }) => {
|
||||
const res = await RestClient.request({
|
||||
method: PUT,
|
||||
url: `/api/${PROVISION}/host/${id}`,
|
||||
data: { amount },
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) {
|
||||
if (res?.id === httpCodes.accepted.id) return res
|
||||
throw res
|
||||
}
|
||||
|
||||
return res?.data ?? {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds more IPs to the provision..
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {object} params.id - Provision id
|
||||
* @param {object} params.amount - Amount of ips to add to the provision
|
||||
* @returns {object} Object of document updated
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
addIp: async ({ id, amount }) => {
|
||||
const res = await RestClient.request({
|
||||
method: PUT,
|
||||
url: `/api/${PROVISION}/ip/${id}`,
|
||||
data: { amount },
|
||||
})
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) {
|
||||
if (res?.id === httpCodes.accepted.id) return res
|
||||
throw res
|
||||
}
|
||||
|
||||
return res?.data ?? {}
|
||||
},
|
||||
}
|
||||
|
@ -1381,7 +1381,7 @@ const hostAdd = (
|
||||
'host',
|
||||
'add',
|
||||
id,
|
||||
'amount',
|
||||
'--amount',
|
||||
amount,
|
||||
...authCommand,
|
||||
...endpoint,
|
||||
@ -1423,7 +1423,7 @@ const ipAdd = (
|
||||
'ip',
|
||||
'add',
|
||||
id,
|
||||
'amount',
|
||||
'--amount',
|
||||
amount,
|
||||
...authCommand,
|
||||
...endpoint,
|
||||
|
@ -287,7 +287,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_HOST_RESOURCE]: {
|
||||
path: `${basepath}/host/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
@ -300,7 +300,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_IMAGE_RESOURCE]: {
|
||||
path: `${basepath}/image/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
@ -313,7 +313,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_NETWORK_RESOURCE]: {
|
||||
path: `${basepath}/network/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
@ -326,7 +326,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_VNTEMPLATE_RESOURCE]: {
|
||||
path: `${basepath}/vntemplate/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
@ -339,7 +339,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_TEMPLATE_RESOURCE]: {
|
||||
path: `${basepath}/template/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
@ -352,7 +352,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
[PROVISION_DELETE_CLUSTER_RESOURCE]: {
|
||||
path: `${basepath}/cluster/:resource/:id`,
|
||||
path: `${basepath}/:resource/:id`,
|
||||
httpMethod: DELETE,
|
||||
auth: true,
|
||||
params: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user