mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-11 04:58:16 +03:00
Co-authored-by: Frederick Borges <fborges@opennebula.io>
This commit is contained in:
parent
201d388601
commit
b06bd9a5ff
install.sh
src/fireedge
etc/sunstone/admin
package-lock.jsonsrc
client
apps/sunstone
components
Cards
FormControl
FormStepper
Forms
Tables
Tabs
constants
containers
features/OneApi
server
routes/api
utils
@ -2970,6 +2970,7 @@ FIREEDGE_SUNSTONE_ETC_VIEW_ADMIN="src/fireedge/etc/sunstone/admin/vm-tab.yaml \
|
|||||||
src/fireedge/etc/sunstone/admin/sec-group-tab.yaml\
|
src/fireedge/etc/sunstone/admin/sec-group-tab.yaml\
|
||||||
src/fireedge/etc/sunstone/admin/backup-tab.yaml \
|
src/fireedge/etc/sunstone/admin/backup-tab.yaml \
|
||||||
src/fireedge/etc/sunstone/admin/datastore-tab.yaml \
|
src/fireedge/etc/sunstone/admin/datastore-tab.yaml \
|
||||||
|
src/fireedge/etc/sunstone/admin/vdc-tab.yaml \
|
||||||
src/fireedge/etc/sunstone/admin/host-tab.yaml"
|
src/fireedge/etc/sunstone/admin/host-tab.yaml"
|
||||||
|
|
||||||
FIREEDGE_SUNSTONE_ETC_VIEW_USER="src/fireedge/etc/sunstone/user/vm-tab.yaml \
|
FIREEDGE_SUNSTONE_ETC_VIEW_USER="src/fireedge/etc/sunstone/user/vm-tab.yaml \
|
||||||
|
56
src/fireedge/etc/sunstone/admin/vdc-tab.yaml
Normal file
56
src/fireedge/etc/sunstone/admin/vdc-tab.yaml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
# This file describes the information and actions available in the VIRTUAL DATA CENTER tab
|
||||||
|
|
||||||
|
# Resource
|
||||||
|
|
||||||
|
resource_name: "VIRTUAL-DATA-CENTER"
|
||||||
|
|
||||||
|
# Actions - Which buttons are visible to operate over the resources
|
||||||
|
|
||||||
|
actions:
|
||||||
|
create_dialog: true
|
||||||
|
update_dialog: true
|
||||||
|
delete: true
|
||||||
|
edit_labels: true
|
||||||
|
|
||||||
|
# Filters - List of criteria to filter the resources
|
||||||
|
|
||||||
|
filters:
|
||||||
|
label: true
|
||||||
|
|
||||||
|
# Info Tabs - Which info tabs are used to show extended information
|
||||||
|
|
||||||
|
info-tabs:
|
||||||
|
info:
|
||||||
|
enabled: true
|
||||||
|
information_panel:
|
||||||
|
enabled: true
|
||||||
|
actions:
|
||||||
|
rename: true
|
||||||
|
attributes_panel:
|
||||||
|
enabled: true
|
||||||
|
actions:
|
||||||
|
copy: true
|
||||||
|
add: true
|
||||||
|
edit: true
|
||||||
|
delete: true
|
||||||
|
|
||||||
|
groups:
|
||||||
|
enabled: true
|
||||||
|
clusters:
|
||||||
|
enabled: true
|
||||||
|
datastores:
|
||||||
|
enabled: true
|
||||||
|
hosts:
|
||||||
|
enabled: true
|
||||||
|
vnets:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
|
||||||
|
# Dialogs
|
||||||
|
dialogs:
|
||||||
|
create_dialog:
|
||||||
|
clusters: true
|
||||||
|
datastores: true
|
||||||
|
hosts: true
|
||||||
|
vnets: true
|
12
src/fireedge/package-lock.json
generated
12
src/fireedge/package-lock.json
generated
@ -3305,9 +3305,9 @@
|
|||||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.0.26",
|
"version": "17.0.58",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.58.tgz",
|
||||||
"integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==",
|
"integrity": "sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
@ -15763,9 +15763,9 @@
|
|||||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
||||||
},
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "18.0.26",
|
"version": "17.0.58",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.58.tgz",
|
||||||
"integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==",
|
"integrity": "sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
EmptyPage as TemplateIcon,
|
EmptyPage as TemplateIcon,
|
||||||
Archive as TemplatesIcon,
|
Archive as TemplatesIcon,
|
||||||
User as UserIcon,
|
User as UserIcon,
|
||||||
|
List as VDCIcon,
|
||||||
Shuffle as VRoutersIcons,
|
Shuffle as VRoutersIcons,
|
||||||
ModernTv as VmsIcons,
|
ModernTv as VmsIcons,
|
||||||
MinusPinAlt as ZoneIcon,
|
MinusPinAlt as ZoneIcon,
|
||||||
@ -103,6 +104,10 @@ const CreateDatastores = loadable(
|
|||||||
ssr: false,
|
ssr: false,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const DatastoreDetail = loadable(
|
||||||
|
() => import('client/containers/Datastores/Detail'),
|
||||||
|
{ ssr: false }
|
||||||
|
)
|
||||||
|
|
||||||
const Images = loadable(() => import('client/containers/Images'), {
|
const Images = loadable(() => import('client/containers/Images'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -199,7 +204,17 @@ const Groups = loadable(() => import('client/containers/Groups'), {
|
|||||||
const GroupDetail = loadable(() => import('client/containers/Groups/Detail'), {
|
const GroupDetail = loadable(() => import('client/containers/Groups/Detail'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
})
|
})
|
||||||
// const VDCs = loadable(() => import('client/containers/VDCs'), { ssr: false })
|
|
||||||
|
const VDCs = loadable(() => import('client/containers/VDCs'), { ssr: false })
|
||||||
|
|
||||||
|
const VDCDetail = loadable(() => import('client/containers/VDCs/Detail'), {
|
||||||
|
ssr: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const VDCCreate = loadable(() => import('client/containers/VDCs/Create'), {
|
||||||
|
ssr: false,
|
||||||
|
})
|
||||||
|
|
||||||
// const ACLs = loadable(() => import('client/containers/ACLs'), { ssr: false })
|
// const ACLs = loadable(() => import('client/containers/ACLs'), { ssr: false })
|
||||||
|
|
||||||
export const PATH = {
|
export const PATH = {
|
||||||
@ -301,6 +316,11 @@ export const PATH = {
|
|||||||
LIST: `/${RESOURCE_NAMES.GROUP}`,
|
LIST: `/${RESOURCE_NAMES.GROUP}`,
|
||||||
DETAIL: `/${RESOURCE_NAMES.GROUP}/:id`,
|
DETAIL: `/${RESOURCE_NAMES.GROUP}/:id`,
|
||||||
},
|
},
|
||||||
|
VDCS: {
|
||||||
|
LIST: `/${RESOURCE_NAMES.VDC}`,
|
||||||
|
DETAIL: `/${RESOURCE_NAMES.VDC}/:id`,
|
||||||
|
CREATE: `/${RESOURCE_NAMES.VDC}/create`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,6 +444,12 @@ const ENDPOINTS = [
|
|||||||
path: PATH.STORAGE.DATASTORES.CREATE,
|
path: PATH.STORAGE.DATASTORES.CREATE,
|
||||||
Component: CreateDatastores,
|
Component: CreateDatastores,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: T.Datastore,
|
||||||
|
description: (params) => `#${params?.id}`,
|
||||||
|
path: PATH.STORAGE.DATASTORES.DETAIL,
|
||||||
|
Component: DatastoreDetail,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: T.Images,
|
title: T.Images,
|
||||||
path: PATH.STORAGE.IMAGES.LIST,
|
path: PATH.STORAGE.IMAGES.LIST,
|
||||||
@ -609,6 +635,31 @@ const ENDPOINTS = [
|
|||||||
path: PATH.SYSTEM.GROUPS.DETAIL,
|
path: PATH.SYSTEM.GROUPS.DETAIL,
|
||||||
Component: GroupDetail,
|
Component: GroupDetail,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: (_, state) =>
|
||||||
|
state?.ID !== undefined ? T.UpdateVDC : T.CreateVDC,
|
||||||
|
path: PATH.SYSTEM.VDCS.CREATE,
|
||||||
|
Component: VDCCreate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: T.VDCs,
|
||||||
|
path: PATH.SYSTEM.VDCS.LIST,
|
||||||
|
sidebar: true,
|
||||||
|
icon: VDCIcon,
|
||||||
|
Component: VDCs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: T.VDC,
|
||||||
|
description: (params) => `#${params?.id}`,
|
||||||
|
path: PATH.SYSTEM.VDCS.DETAIL,
|
||||||
|
Component: VDCDetail,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: T.Group,
|
||||||
|
// description: (params) => `#${params?.id}`,
|
||||||
|
// path: PATH.SYSTEM.GROUPS.DETAIL,
|
||||||
|
// Component: GroupDetail,
|
||||||
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,173 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 PropTypes from 'prop-types'
|
||||||
|
import { ReactElement, memo, useMemo } from 'react'
|
||||||
|
|
||||||
|
import { Typography } from '@mui/material'
|
||||||
|
import {
|
||||||
|
Server as ClusterIcon,
|
||||||
|
Db as DatastoreIcon,
|
||||||
|
Group as GroupIcon,
|
||||||
|
HardDrive as HostIcon,
|
||||||
|
Network as VNetIcon,
|
||||||
|
} from 'iconoir-react'
|
||||||
|
|
||||||
|
import MultipleTags from 'client/components/MultipleTags'
|
||||||
|
import { useViews } from 'client/features/Auth'
|
||||||
|
|
||||||
|
import { Tr } from 'client/components/HOC'
|
||||||
|
import { StatusCircle } from 'client/components/Status'
|
||||||
|
import { rowStyles } from 'client/components/Tables/styles'
|
||||||
|
|
||||||
|
import { ACTIONS, ALL_SELECTED, RESOURCE_NAMES, T, VDC } from 'client/constants'
|
||||||
|
import COLOR from 'client/constants/color'
|
||||||
|
import { getColorFromString, getUniqueLabels } from 'client/models/Helper'
|
||||||
|
|
||||||
|
const isAllSelected = (resourceArray) =>
|
||||||
|
resourceArray.length === 1 && resourceArray[0] === ALL_SELECTED
|
||||||
|
|
||||||
|
const VirtualDataCenterCard = memo(
|
||||||
|
/**
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {VDC} props.template - Virtual Data Center resource
|
||||||
|
* @param {object} props.rootProps - Props to root component
|
||||||
|
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
|
||||||
|
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
|
||||||
|
* @returns {ReactElement} - Card
|
||||||
|
*/
|
||||||
|
({ template, rootProps, onClickLabel, onDeleteLabel }) => {
|
||||||
|
const classes = rowStyles()
|
||||||
|
const { [RESOURCE_NAMES.VDC]: vdcView } = useViews()
|
||||||
|
|
||||||
|
const enableEditLabels =
|
||||||
|
vdcView?.actions?.[ACTIONS.EDIT_LABELS] === true && !!onDeleteLabel
|
||||||
|
|
||||||
|
const {
|
||||||
|
ID,
|
||||||
|
NAME,
|
||||||
|
GROUPS,
|
||||||
|
CLUSTERS,
|
||||||
|
HOSTS,
|
||||||
|
DATASTORES,
|
||||||
|
VNETS,
|
||||||
|
TEMPLATE: { LABELS },
|
||||||
|
} = template
|
||||||
|
|
||||||
|
const groupsCount = useMemo(() => {
|
||||||
|
const { ID: groupsIds = [] } = GROUPS
|
||||||
|
const groupsArray = Array.isArray(groupsIds) ? groupsIds : [groupsIds]
|
||||||
|
|
||||||
|
return groupsArray.length
|
||||||
|
}, [GROUPS.ID])
|
||||||
|
|
||||||
|
const clustersCount = useMemo(() => {
|
||||||
|
const { CLUSTER: clustersInfo = [] } = CLUSTERS
|
||||||
|
const clustersArray = (
|
||||||
|
Array.isArray(clustersInfo) ? clustersInfo : [clustersInfo]
|
||||||
|
).map((cluster) => cluster.CLUSTER_ID)
|
||||||
|
|
||||||
|
return isAllSelected(clustersArray) ? T.All : clustersArray.length
|
||||||
|
}, [CLUSTERS.CLUSTER])
|
||||||
|
|
||||||
|
const hostsCount = useMemo(() => {
|
||||||
|
const { HOST: hostsInfo = [] } = HOSTS
|
||||||
|
const hostsArray = (
|
||||||
|
Array.isArray(hostsInfo) ? hostsInfo : [hostsInfo]
|
||||||
|
).map((host) => host.HOST_ID)
|
||||||
|
|
||||||
|
return isAllSelected(hostsArray) ? T.All : hostsArray.length
|
||||||
|
}, [HOSTS.HOST])
|
||||||
|
|
||||||
|
const datastoresCount = useMemo(() => {
|
||||||
|
const { DATASTORE: datastoresInfo = [] } = DATASTORES
|
||||||
|
const datastoresArray = (
|
||||||
|
Array.isArray(datastoresInfo) ? datastoresInfo : [datastoresInfo]
|
||||||
|
).map((ds) => ds.DATASTORE_ID)
|
||||||
|
|
||||||
|
return isAllSelected(datastoresArray) ? T.All : datastoresArray.length
|
||||||
|
}, [DATASTORES.DATASTORE])
|
||||||
|
|
||||||
|
const vnetsCount = useMemo(() => {
|
||||||
|
const { VNET: vnetsInfo = [] } = VNETS
|
||||||
|
const vnetsArray = (
|
||||||
|
Array.isArray(vnetsInfo) ? vnetsInfo : [vnetsInfo]
|
||||||
|
).map((vnet) => vnet.VNET_ID)
|
||||||
|
|
||||||
|
return isAllSelected(vnetsArray) ? T.All : vnetsArray.length
|
||||||
|
}, [VNETS.VNET])
|
||||||
|
|
||||||
|
const labels = useMemo(
|
||||||
|
() =>
|
||||||
|
getUniqueLabels(LABELS).map((label) => ({
|
||||||
|
text: label,
|
||||||
|
stateColor: getColorFromString(label),
|
||||||
|
onClick: onClickLabel,
|
||||||
|
onDelete: enableEditLabels && onDeleteLabel,
|
||||||
|
})),
|
||||||
|
[LABELS, enableEditLabels, onClickLabel, onDeleteLabel]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...rootProps} data-cy={`vdc-${ID}`}>
|
||||||
|
<div className={classes.main}>
|
||||||
|
<div className={classes.title}>
|
||||||
|
<StatusCircle color={COLOR.success.main} />
|
||||||
|
<Typography component="span">{NAME}</Typography>
|
||||||
|
<span className={classes.labels}>
|
||||||
|
<MultipleTags tags={labels} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={classes.caption}>
|
||||||
|
<span>{`#${ID}`}</span>
|
||||||
|
<span title={`${Tr(T.Groups)}: ${groupsCount}`}>
|
||||||
|
<GroupIcon />
|
||||||
|
<span>{` ${groupsCount}`}</span>
|
||||||
|
</span>
|
||||||
|
<span title={`${Tr(T.Clusters)}: ${clustersCount}`}>
|
||||||
|
<ClusterIcon />
|
||||||
|
<span>{` ${clustersCount}`}</span>
|
||||||
|
</span>
|
||||||
|
<span title={`${Tr(T.Hosts)}: ${hostsCount}`}>
|
||||||
|
<HostIcon />
|
||||||
|
<span>{` ${hostsCount}`}</span>
|
||||||
|
</span>
|
||||||
|
<span title={`${Tr(T.Datastores)}: ${datastoresCount}`}>
|
||||||
|
<DatastoreIcon />
|
||||||
|
<span>{` ${datastoresCount}`}</span>
|
||||||
|
</span>
|
||||||
|
<span title={`${Tr(T.VirtualNetworks)}: ${vnetsCount}`}>
|
||||||
|
<VNetIcon />
|
||||||
|
<span>{` ${vnetsCount}`}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
VirtualDataCenterCard.propTypes = {
|
||||||
|
template: PropTypes.object,
|
||||||
|
rootProps: PropTypes.shape({
|
||||||
|
className: PropTypes.string,
|
||||||
|
}),
|
||||||
|
onClickLabel: PropTypes.func,
|
||||||
|
onDeleteLabel: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDataCenterCard.displayName = 'VirtualDataCenterCard'
|
||||||
|
|
||||||
|
export default VirtualDataCenterCard
|
@ -39,6 +39,7 @@ import ServiceCard from 'client/components/Cards/ServiceCard'
|
|||||||
import ServiceTemplateCard from 'client/components/Cards/ServiceTemplateCard'
|
import ServiceTemplateCard from 'client/components/Cards/ServiceTemplateCard'
|
||||||
import SnapshotCard from 'client/components/Cards/SnapshotCard'
|
import SnapshotCard from 'client/components/Cards/SnapshotCard'
|
||||||
import TierCard from 'client/components/Cards/TierCard'
|
import TierCard from 'client/components/Cards/TierCard'
|
||||||
|
import VirtualDataCenterCard from 'client/components/Cards/VirtualDataCenterCard'
|
||||||
import VirtualMachineCard from 'client/components/Cards/VirtualMachineCard'
|
import VirtualMachineCard from 'client/components/Cards/VirtualMachineCard'
|
||||||
import VmTemplateCard from 'client/components/Cards/VmTemplateCard'
|
import VmTemplateCard from 'client/components/Cards/VmTemplateCard'
|
||||||
import WavesCard from 'client/components/Cards/WavesCard'
|
import WavesCard from 'client/components/Cards/WavesCard'
|
||||||
@ -70,6 +71,7 @@ export {
|
|||||||
ServiceTemplateCard,
|
ServiceTemplateCard,
|
||||||
SnapshotCard,
|
SnapshotCard,
|
||||||
TierCard,
|
TierCard,
|
||||||
|
VirtualDataCenterCard,
|
||||||
VirtualMachineCard,
|
VirtualMachineCard,
|
||||||
VmTemplateCard,
|
VmTemplateCard,
|
||||||
WavesCard,
|
WavesCard,
|
||||||
|
@ -13,12 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and *
|
* See the License for the specific language governing permissions and *
|
||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useFormContext, useController } from 'react-hook-form'
|
import { memo, useCallback, useEffect, useState } from 'react'
|
||||||
|
import { useController, useFormContext } from 'react-hook-form'
|
||||||
|
|
||||||
import Legend from 'client/components/Forms/Legend'
|
|
||||||
import { ErrorHelper } from 'client/components/FormControl'
|
import { ErrorHelper } from 'client/components/FormControl'
|
||||||
|
import Legend from 'client/components/Forms/Legend'
|
||||||
|
import { sortStateTables } from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
|
||||||
import { generateKey } from 'client/utils'
|
import { generateKey } from 'client/utils'
|
||||||
|
|
||||||
const defaultGetRowId = (item) =>
|
const defaultGetRowId = (item) =>
|
||||||
@ -44,7 +45,9 @@ const TableController = memo(
|
|||||||
getRowId = defaultGetRowId,
|
getRowId = defaultGetRowId,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
onConditionChange,
|
onConditionChange,
|
||||||
fieldProps: { initialState, ...fieldProps } = {},
|
zoneId,
|
||||||
|
dependOf,
|
||||||
|
fieldProps: { initialState, preserveState, ...fieldProps } = {},
|
||||||
}) => {
|
}) => {
|
||||||
const { clearErrors } = useFormContext()
|
const { clearErrors } = useFormContext()
|
||||||
|
|
||||||
@ -57,9 +60,15 @@ const TableController = memo(
|
|||||||
getSelectedRowIds(value)
|
getSelectedRowIds(value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const reSelectRows = (newValues = []) => {
|
||||||
|
const sortedNewValues = sortStateTables(newValues)
|
||||||
|
onChange(sortedNewValues)
|
||||||
|
setInitialRows(getSelectedRowIds(sortedNewValues))
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange(singleSelect ? undefined : [])
|
onChange(singleSelect ? undefined : preserveState ? value : [])
|
||||||
setInitialRows({})
|
setInitialRows(preserveState ? initialRows : {})
|
||||||
}, [Table])
|
}, [Table])
|
||||||
|
|
||||||
const handleSelectedRowsChange = useCallback(
|
const handleSelectedRowsChange = useCallback(
|
||||||
@ -99,8 +108,12 @@ const TableController = memo(
|
|||||||
disableRowSelect={readOnly}
|
disableRowSelect={readOnly}
|
||||||
singleSelect={singleSelect}
|
singleSelect={singleSelect}
|
||||||
getRowId={getRowId}
|
getRowId={getRowId}
|
||||||
|
zoneId={zoneId}
|
||||||
|
dependOf={dependOf}
|
||||||
initialState={{ ...initialState, selectedRowIds: initialRows }}
|
initialState={{ ...initialState, selectedRowIds: initialRows }}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
value={value ?? []}
|
||||||
|
reSelectRows={reSelectRows}
|
||||||
{...fieldProps}
|
{...fieldProps}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
@ -116,6 +129,7 @@ TableController.propTypes = {
|
|||||||
control: PropTypes.object,
|
control: PropTypes.object,
|
||||||
cy: PropTypes.string,
|
cy: PropTypes.string,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
|
zoneId: PropTypes.string,
|
||||||
singleSelect: PropTypes.bool,
|
singleSelect: PropTypes.bool,
|
||||||
Table: PropTypes.any,
|
Table: PropTypes.any,
|
||||||
getRowId: PropTypes.func,
|
getRowId: PropTypes.func,
|
||||||
@ -124,7 +138,6 @@ TableController.propTypes = {
|
|||||||
tooltip: PropTypes.any,
|
tooltip: PropTypes.any,
|
||||||
fieldProps: PropTypes.object,
|
fieldProps: PropTypes.object,
|
||||||
readOnly: PropTypes.bool,
|
readOnly: PropTypes.bool,
|
||||||
onConditionChange: PropTypes.func,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TableController.displayName = 'TableController'
|
TableController.displayName = 'TableController'
|
||||||
|
@ -17,19 +17,19 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { Box, Button, Typography } from '@mui/material'
|
import { Box, Button, Typography } from '@mui/material'
|
||||||
import Stepper from '@mui/material/Stepper'
|
|
||||||
import Step from '@mui/material/Step'
|
import Step from '@mui/material/Step'
|
||||||
import StepLabel from '@mui/material/StepLabel'
|
|
||||||
import StepButton from '@mui/material/StepButton'
|
import StepButton from '@mui/material/StepButton'
|
||||||
import StepIcon, { stepIconClasses } from '@mui/material/StepIcon'
|
|
||||||
import StepConnector, {
|
import StepConnector, {
|
||||||
stepConnectorClasses,
|
stepConnectorClasses,
|
||||||
} from '@mui/material/StepConnector'
|
} from '@mui/material/StepConnector'
|
||||||
|
import StepIcon, { stepIconClasses } from '@mui/material/StepIcon'
|
||||||
|
import StepLabel from '@mui/material/StepLabel'
|
||||||
|
import Stepper from '@mui/material/Stepper'
|
||||||
import { styled } from '@mui/styles'
|
import { styled } from '@mui/styles'
|
||||||
|
|
||||||
import { SubmitButton } from 'client/components/FormControl'
|
import { SubmitButton } from 'client/components/FormControl'
|
||||||
import { Translate } from 'client/components/HOC'
|
import { Translate } from 'client/components/HOC'
|
||||||
import { T, SCHEMES } from 'client/constants'
|
import { SCHEMES, T } from 'client/constants'
|
||||||
|
|
||||||
const StepperStyled = styled(Stepper)(({ theme }) => ({
|
const StepperStyled = styled(Stepper)(({ theme }) => ({
|
||||||
backdropFilter: 'blur(3px)',
|
backdropFilter: 'blur(3px)',
|
||||||
|
@ -28,10 +28,10 @@ import { Accordion, AccordionSummary, FormControl, Grid } from '@mui/material'
|
|||||||
import { useFormContext, useWatch } from 'react-hook-form'
|
import { useFormContext, useWatch } from 'react-hook-form'
|
||||||
|
|
||||||
import * as FC from 'client/components/FormControl'
|
import * as FC from 'client/components/FormControl'
|
||||||
|
import { useDisableStep } from 'client/components/FormStepper'
|
||||||
import Legend from 'client/components/Forms/Legend'
|
import Legend from 'client/components/Forms/Legend'
|
||||||
import { INPUT_TYPES } from 'client/constants'
|
import { INPUT_TYPES } from 'client/constants'
|
||||||
import { Field } from 'client/utils'
|
import { Field } from 'client/utils'
|
||||||
import { useDisableStep } from 'client/components/FormStepper'
|
|
||||||
|
|
||||||
const NOT_DEPEND_ATTRIBUTES = [
|
const NOT_DEPEND_ATTRIBUTES = [
|
||||||
'watcher',
|
'watcher',
|
||||||
@ -230,6 +230,7 @@ const FieldComponent = memo(
|
|||||||
dependencies: nameOfDependField,
|
dependencies: nameOfDependField,
|
||||||
name: inputName,
|
name: inputName,
|
||||||
type: htmlType === false ? undefined : htmlType,
|
type: htmlType === false ? undefined : htmlType,
|
||||||
|
dependOf,
|
||||||
onConditionChange: handleConditionChange,
|
onConditionChange: handleConditionChange,
|
||||||
...fieldProps,
|
...fieldProps,
|
||||||
})}
|
})}
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
// import { Stack } from '@mui/material'
|
// import { Stack } from '@mui/material'
|
||||||
import { useFormContext, useWatch } from 'react-hook-form'
|
|
||||||
import SecurityIcon from 'iconoir-react/dist/HistoricShield'
|
import SecurityIcon from 'iconoir-react/dist/HistoricShield'
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form'
|
||||||
|
|
||||||
import { SecurityGroupsTable } from 'client/components/Tables'
|
import { SecurityGroupsTable } from 'client/components/Tables'
|
||||||
|
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { Box } from '@mui/material'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form'
|
||||||
|
import { object } from 'yup'
|
||||||
|
|
||||||
|
import { AttributePanel } from 'client/components/Tabs/Common'
|
||||||
|
import { T } from 'client/constants'
|
||||||
|
import { cleanEmpty, cloneObject, set } from 'client/utils'
|
||||||
|
|
||||||
|
export const STEP_ID = 'custom-variables'
|
||||||
|
|
||||||
|
const Content = () => {
|
||||||
|
const { setValue } = useFormContext()
|
||||||
|
const customVars = useWatch({ name: STEP_ID })
|
||||||
|
|
||||||
|
const handleChangeAttribute = useCallback(
|
||||||
|
(path, newValue) => {
|
||||||
|
const newCustomVars = cloneObject(customVars)
|
||||||
|
set(newCustomVars, path, newValue)
|
||||||
|
setValue(STEP_ID, cleanEmpty(newCustomVars))
|
||||||
|
},
|
||||||
|
[customVars]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="grid" gap="1em">
|
||||||
|
<AttributePanel
|
||||||
|
allActionsEnabled
|
||||||
|
handleAdd={handleChangeAttribute}
|
||||||
|
handleEdit={handleChangeAttribute}
|
||||||
|
handleDelete={handleChangeAttribute}
|
||||||
|
attributes={customVars}
|
||||||
|
filtersSpecialAttributes={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom variables about VDC Template.
|
||||||
|
*
|
||||||
|
* @returns {object} Custom configuration step
|
||||||
|
*/
|
||||||
|
const CustomVariables = () => ({
|
||||||
|
id: STEP_ID,
|
||||||
|
label: T.CustomVariables,
|
||||||
|
resolver: object(),
|
||||||
|
optionsValidate: { abortEarly: false },
|
||||||
|
content: Content,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default CustomVariables
|
@ -0,0 +1,41 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
|
||||||
|
import { T } from 'client/constants'
|
||||||
|
import { FIELDS, SCHEMA } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'general'
|
||||||
|
|
||||||
|
const Content = () => (
|
||||||
|
<FormWithSchema id={STEP_ID} fields={FIELDS} cy={`${STEP_ID}`} />
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General configuration about VDC Template.
|
||||||
|
*
|
||||||
|
* @returns {object} General configuration step
|
||||||
|
*/
|
||||||
|
const General = () => ({
|
||||||
|
id: STEP_ID,
|
||||||
|
label: T.Configuration,
|
||||||
|
resolver: SCHEMA,
|
||||||
|
optionsValidate: { abortEarly: false },
|
||||||
|
content: Content,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default General
|
@ -0,0 +1,48 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, object, string } from 'yup'
|
||||||
|
|
||||||
|
/** @type {Field} name field */
|
||||||
|
const NAME = {
|
||||||
|
name: 'NAME',
|
||||||
|
label: T.Name,
|
||||||
|
type: INPUT_TYPES.TEXT,
|
||||||
|
validation: string().trim().required(),
|
||||||
|
grid: { xs: 12, md: 12 },
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Field} Description field */
|
||||||
|
const DESCRIPTION = {
|
||||||
|
name: 'DESCRIPTION',
|
||||||
|
label: T.Description,
|
||||||
|
type: INPUT_TYPES.TEXT,
|
||||||
|
multiline: true,
|
||||||
|
validation: string().trim(),
|
||||||
|
grid: { xs: 12, md: 12 },
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = [NAME, DESCRIPTION]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} [stepProps] - Step props
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,70 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 PropTypes from 'prop-types'
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form'
|
||||||
|
|
||||||
|
import { GroupsTable } from 'client/components/Tables'
|
||||||
|
import { SCHEMA } from './schema'
|
||||||
|
|
||||||
|
import { T } from 'client/constants'
|
||||||
|
import { Step } from 'client/utils'
|
||||||
|
|
||||||
|
export const STEP_ID = 'groups'
|
||||||
|
|
||||||
|
const Content = () => {
|
||||||
|
const { setValue } = useFormContext()
|
||||||
|
const groups = useWatch({ name: STEP_ID })
|
||||||
|
|
||||||
|
const selectedRowIds =
|
||||||
|
groups?.reduce((res, id) => ({ ...res, [id]: true }), {}) || []
|
||||||
|
|
||||||
|
const handleSelectedRows = (rows) => {
|
||||||
|
const newValue = rows?.map((row) => row.original.ID) || []
|
||||||
|
|
||||||
|
setValue(STEP_ID, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GroupsTable
|
||||||
|
disableGlobalSort
|
||||||
|
displaySelectedRows
|
||||||
|
pageSize={5}
|
||||||
|
initialState={{ selectedRowIds }}
|
||||||
|
onSelectedRowsChange={handleSelectedRows}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step to select the Group.
|
||||||
|
*
|
||||||
|
* @param {object} app - VDC App resource
|
||||||
|
* @returns {Step} Group step
|
||||||
|
*/
|
||||||
|
const GroupsStep = (app) => ({
|
||||||
|
id: STEP_ID,
|
||||||
|
label: T.SelectGroup,
|
||||||
|
resolver: SCHEMA,
|
||||||
|
content: (props) => Content({ ...props, app }),
|
||||||
|
})
|
||||||
|
|
||||||
|
Content.propTypes = {
|
||||||
|
data: PropTypes.any,
|
||||||
|
setFormData: PropTypes.func,
|
||||||
|
app: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupsStep
|
@ -0,0 +1,19 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ArraySchema, array, string } from 'yup'
|
||||||
|
|
||||||
|
/** @type {ArraySchema} Datastore table schema */
|
||||||
|
export const SCHEMA = array(string().trim()).default(() => [])
|
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/index.js
Normal file
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/index.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { FIELDS } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'clusters'
|
||||||
|
|
||||||
|
const ClusterTable = ({ zones, id }) => (
|
||||||
|
<FormWithSchema id={id} cy={`${STEP_ID}`} fields={FIELDS(zones)} />
|
||||||
|
)
|
||||||
|
|
||||||
|
ClusterTable.propTypes = {
|
||||||
|
zones: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
const ClusterResource = {
|
||||||
|
id: STEP_ID,
|
||||||
|
Content: ClusterTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ClusterResource
|
61
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/schema.js
Normal file
61
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/schema.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ZONE_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema'
|
||||||
|
import { ClustersTable } from 'client/components/Tables'
|
||||||
|
import { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, array, object, string } from 'yup'
|
||||||
|
|
||||||
|
export const CLUSTER_FIELD_NAME = 'CLUSTER_Z'
|
||||||
|
|
||||||
|
/** @type {Field} Cluster field */
|
||||||
|
const CLUSTER = (zoneId) => ({
|
||||||
|
name: `${CLUSTER_FIELD_NAME}${zoneId}`,
|
||||||
|
dependOf: `$resources.${ZONE_FIELD_NAME}`,
|
||||||
|
htmlType: ([_, selectedZoneId = '0'] = []) =>
|
||||||
|
zoneId !== selectedZoneId && INPUT_TYPES.HIDDEN,
|
||||||
|
label: T.SelectClusters,
|
||||||
|
type: INPUT_TYPES.TABLE,
|
||||||
|
Table: () => ClustersTable,
|
||||||
|
singleSelect: false,
|
||||||
|
zoneId: zoneId,
|
||||||
|
validation: array(string().trim()).default(() => []),
|
||||||
|
fieldProps: {
|
||||||
|
preserveState: true,
|
||||||
|
},
|
||||||
|
grid: { md: 12 },
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = (zones = []) => {
|
||||||
|
const fields = []
|
||||||
|
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
fields.push(CLUSTER(zone.ID))
|
||||||
|
})
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object(getValidationFromFields(FIELDS(zones)))
|
41
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/DatastoresTable/index.js
Normal file
41
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/DatastoresTable/index.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { FIELDS } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'datastores'
|
||||||
|
|
||||||
|
const DatastoreTable = ({ zones, id, cluster, zone }) => (
|
||||||
|
<FormWithSchema
|
||||||
|
id={id}
|
||||||
|
cy={`${STEP_ID}`}
|
||||||
|
fields={FIELDS(zones, cluster, zone)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
DatastoreTable.propTypes = {
|
||||||
|
zones: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
id: PropTypes.string,
|
||||||
|
zone: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
cluster: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
}
|
||||||
|
const DatastoreResource = {
|
||||||
|
id: STEP_ID,
|
||||||
|
Content: DatastoreTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatastoreResource
|
76
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/DatastoresTable/schema.js
Normal file
76
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/DatastoresTable/schema.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { CLUSTER_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/schema'
|
||||||
|
import { ZONE_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema'
|
||||||
|
import { DatastoresTable } from 'client/components/Tables'
|
||||||
|
import { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, array, object, string } from 'yup'
|
||||||
|
|
||||||
|
/** @type {Field} Cluster field */
|
||||||
|
const DATASTORE = (zoneId) => ({
|
||||||
|
name: `DATASTORE_Z${zoneId}`,
|
||||||
|
dependOf: [
|
||||||
|
`$resources.${ZONE_FIELD_NAME}`,
|
||||||
|
`$resources.${CLUSTER_FIELD_NAME}${zoneId}`,
|
||||||
|
],
|
||||||
|
htmlType: ([selectedZoneId = '0'] = []) =>
|
||||||
|
zoneId !== selectedZoneId && INPUT_TYPES.HIDDEN,
|
||||||
|
label: T.SelectDatastores,
|
||||||
|
type: INPUT_TYPES.TABLE,
|
||||||
|
Table: () => DatastoresTable,
|
||||||
|
singleSelect: false,
|
||||||
|
zoneId: zoneId,
|
||||||
|
validation: array(string().trim()).default(() => []),
|
||||||
|
fieldProps: {
|
||||||
|
preserveState: true,
|
||||||
|
|
||||||
|
// The second parameter of the function filters the results that are found in dependOf
|
||||||
|
filter: (data, [_, clusters]) =>
|
||||||
|
clusters?.length
|
||||||
|
? data.filter((item) => {
|
||||||
|
const dataClusters = item.CLUSTERS.ID
|
||||||
|
if (Array.isArray(dataClusters)) {
|
||||||
|
return dataClusters.some((id) => !clusters.includes(id))
|
||||||
|
} else {
|
||||||
|
return !clusters.includes(dataClusters)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: data,
|
||||||
|
},
|
||||||
|
grid: { md: 12 },
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = (zones = []) => {
|
||||||
|
const fields = []
|
||||||
|
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
fields.push(DATASTORE(zone.ID))
|
||||||
|
})
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object(getValidationFromFields(FIELDS(zones)))
|
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/HostsTable/index.js
Normal file
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/HostsTable/index.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { FIELDS } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'hosts'
|
||||||
|
|
||||||
|
const HostTable = ({ zones, id }) => (
|
||||||
|
<FormWithSchema id={id} cy={`${STEP_ID}`} fields={FIELDS(zones)} />
|
||||||
|
)
|
||||||
|
|
||||||
|
HostTable.propTypes = {
|
||||||
|
zones: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
const HostResource = {
|
||||||
|
id: STEP_ID,
|
||||||
|
Content: HostTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HostResource
|
75
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/HostsTable/schema.js
Normal file
75
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/HostsTable/schema.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { CLUSTER_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/schema'
|
||||||
|
import { ZONE_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema'
|
||||||
|
import { HostsTable } from 'client/components/Tables'
|
||||||
|
import { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, array, object, string } from 'yup'
|
||||||
|
|
||||||
|
/** @type {Field} Cluster field */
|
||||||
|
const HOST = (zoneId) => ({
|
||||||
|
name: `HOST_Z${zoneId}`,
|
||||||
|
dependOf: [
|
||||||
|
`$resources.${ZONE_FIELD_NAME}`,
|
||||||
|
`$resources.${CLUSTER_FIELD_NAME}${zoneId}`,
|
||||||
|
],
|
||||||
|
htmlType: ([selectedZoneId = '0'] = []) =>
|
||||||
|
zoneId !== selectedZoneId && INPUT_TYPES.HIDDEN,
|
||||||
|
label: T.SelectHosts,
|
||||||
|
type: INPUT_TYPES.TABLE,
|
||||||
|
Table: () => HostsTable,
|
||||||
|
singleSelect: false,
|
||||||
|
zoneId: zoneId,
|
||||||
|
validation: array(string().trim()).default(() => []),
|
||||||
|
fieldProps: {
|
||||||
|
preserveState: true,
|
||||||
|
// The second parameter of the function filters the results that are found in dependOf
|
||||||
|
filter: (data, [_, clusters]) =>
|
||||||
|
clusters?.length
|
||||||
|
? data.filter((item) => {
|
||||||
|
const dataClusters = item.CLUSTER_ID
|
||||||
|
if (Array.isArray(dataClusters)) {
|
||||||
|
return dataClusters.some((id) => !clusters.includes(id))
|
||||||
|
} else {
|
||||||
|
return !clusters.includes(dataClusters)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: data,
|
||||||
|
},
|
||||||
|
grid: { md: 12 },
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = (zones = []) => {
|
||||||
|
const fields = []
|
||||||
|
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
fields.push(HOST(zone.ID))
|
||||||
|
})
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object(getValidationFromFields(FIELDS(zones)))
|
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/VnetsTable/index.js
Normal file
35
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/VnetsTable/index.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { FIELDS } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'vnets'
|
||||||
|
|
||||||
|
const VnetTable = ({ zones, id }) => (
|
||||||
|
<FormWithSchema id={id} cy={`${STEP_ID}`} fields={FIELDS(zones)} />
|
||||||
|
)
|
||||||
|
|
||||||
|
VnetTable.propTypes = {
|
||||||
|
zones: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
const VnetResource = {
|
||||||
|
id: STEP_ID,
|
||||||
|
Content: VnetTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VnetResource
|
77
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/VnetsTable/schema.js
Normal file
77
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/VnetsTable/schema.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { CLUSTER_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ClustersTable/schema'
|
||||||
|
import { ZONE_FIELD_NAME } from 'client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema'
|
||||||
|
import { VNetworksTable } from 'client/components/Tables'
|
||||||
|
import { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, array, object, string } from 'yup'
|
||||||
|
|
||||||
|
// const ZONE_FIELD_NAME = 'VNET_ZONE_ID'
|
||||||
|
|
||||||
|
/** @type {Field} Cluster field */
|
||||||
|
const VNET = (zoneId) => ({
|
||||||
|
name: `VNET_Z${zoneId}`,
|
||||||
|
dependOf: [
|
||||||
|
`$resources.${ZONE_FIELD_NAME}`,
|
||||||
|
`$resources.${CLUSTER_FIELD_NAME}${zoneId}`,
|
||||||
|
],
|
||||||
|
htmlType: ([selectedZoneId = '0'] = []) =>
|
||||||
|
zoneId !== selectedZoneId && INPUT_TYPES.HIDDEN,
|
||||||
|
label: T.SelectVirtualNetworks,
|
||||||
|
type: INPUT_TYPES.TABLE,
|
||||||
|
Table: () => VNetworksTable,
|
||||||
|
singleSelect: false,
|
||||||
|
zoneId: zoneId,
|
||||||
|
validation: array(string().trim()).default(() => []),
|
||||||
|
fieldProps: {
|
||||||
|
preserveState: true,
|
||||||
|
// The second parameter of the function filters the results that are found in dependOf
|
||||||
|
filter: (data, [_, clusters]) =>
|
||||||
|
clusters?.length
|
||||||
|
? data.filter((item) => {
|
||||||
|
const dataClusters = item.CLUSTERS.ID
|
||||||
|
if (Array.isArray(dataClusters)) {
|
||||||
|
return dataClusters.some((id) => !clusters.includes(id))
|
||||||
|
} else {
|
||||||
|
return !clusters.includes(dataClusters)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: data,
|
||||||
|
},
|
||||||
|
grid: { md: 12 },
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = (zones = []) => {
|
||||||
|
const fields = []
|
||||||
|
|
||||||
|
zones.forEach((zone) => {
|
||||||
|
fields.push(VNET(zone.ID))
|
||||||
|
})
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object(getValidationFromFields(FIELDS(zones)))
|
38
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/index.js
Normal file
38
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/index.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { JSXElementConstructor } from 'react'
|
||||||
|
import { FIELDS } from './schema'
|
||||||
|
|
||||||
|
export const STEP_ID = 'zone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} props - props
|
||||||
|
* @param {Array} props.zones - zones
|
||||||
|
* @param {string} props.id - form id
|
||||||
|
* @returns {JSXElementConstructor} Provision App
|
||||||
|
*/
|
||||||
|
const ZoneSelect = ({ zones, id }) => (
|
||||||
|
<FormWithSchema id={id} cy={`${STEP_ID}`} fields={FIELDS(zones)} />
|
||||||
|
)
|
||||||
|
|
||||||
|
ZoneSelect.propTypes = {
|
||||||
|
zones: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ZoneSelect
|
47
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema.js
Normal file
47
src/fireedge/src/client/components/Forms/Vdc/CreateForm/Steps/Resources/ZonesSelect/schema.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { INPUT_TYPES, T } from 'client/constants'
|
||||||
|
import { Field, arrayToOptions, getValidationFromFields } from 'client/utils'
|
||||||
|
import { ObjectSchema, object, string } from 'yup'
|
||||||
|
|
||||||
|
export const ZONE_FIELD_NAME = 'ZONE_ID'
|
||||||
|
|
||||||
|
/** @type {Field} Zone Id field */
|
||||||
|
const ZONE = (zones) => ({
|
||||||
|
name: ZONE_FIELD_NAME,
|
||||||
|
label: T.Zone,
|
||||||
|
type: INPUT_TYPES.SELECT,
|
||||||
|
values: arrayToOptions(zones, {
|
||||||
|
addEmpty: false,
|
||||||
|
getText: (zone) => `Zone ${zone.ID} - ${zone.NAME}`,
|
||||||
|
getValue: (zone) => zone.ID,
|
||||||
|
}),
|
||||||
|
validation: string().default(() => '0'),
|
||||||
|
grid: { xs: 12, md: 12 },
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {Field[]} Fields
|
||||||
|
*/
|
||||||
|
export const FIELDS = (zones = []) => [ZONE(zones)]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - zone objects
|
||||||
|
* @returns {ObjectSchema} Schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object(getValidationFromFields(FIELDS(zones)))
|
@ -0,0 +1,103 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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-next-line no-unused-vars
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { ReactElement, useMemo } from 'react'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { FieldErrors, useFormContext } from 'react-hook-form'
|
||||||
|
|
||||||
|
import { useViews } from 'client/features/Auth'
|
||||||
|
|
||||||
|
import Cluster from './ClustersTable'
|
||||||
|
import Datastore from './DatastoresTable'
|
||||||
|
import Host from './HostsTable'
|
||||||
|
import Vnets from './VnetsTable'
|
||||||
|
import ZoneSelect from './ZonesSelect'
|
||||||
|
|
||||||
|
import { SCHEMA } from './schema'
|
||||||
|
|
||||||
|
import { RESOURCE_NAMES, T } from 'client/constants'
|
||||||
|
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} TabType
|
||||||
|
* @property {string} id - Id will be to use in view yaml to hide/display the tab
|
||||||
|
* @property {string} name - Label of tab
|
||||||
|
* @property {ReactElement} Content - Content tab
|
||||||
|
* @property {object} [icon] - Icon of tab
|
||||||
|
* @property {function(FieldErrors):boolean} [getError] - Returns `true` if the tab contains an error in form
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const STEP_ID = 'resources'
|
||||||
|
|
||||||
|
/** @type {TabType[]} */
|
||||||
|
export const RESOURCES = [Cluster, Datastore, Host, Vnets]
|
||||||
|
|
||||||
|
const Content = (zones) => {
|
||||||
|
const {
|
||||||
|
formState: { errors },
|
||||||
|
control,
|
||||||
|
} = useFormContext()
|
||||||
|
const { view, getResourceView } = useViews()
|
||||||
|
|
||||||
|
const sectionsAvailable = useMemo(() => {
|
||||||
|
const resource = RESOURCE_NAMES.VDC
|
||||||
|
const dialog = getResourceView(resource)?.dialogs?.create_dialog
|
||||||
|
|
||||||
|
return getSectionsAvailable(dialog)
|
||||||
|
}, [view])
|
||||||
|
|
||||||
|
const totalErrors = Object.keys(errors[STEP_ID] ?? {}).length
|
||||||
|
|
||||||
|
const resources = useMemo(
|
||||||
|
() =>
|
||||||
|
RESOURCES.filter(({ id }) => sectionsAvailable.includes(id)).map(
|
||||||
|
({ Content: TabContent, id }) => (
|
||||||
|
<TabContent key={id} id={STEP_ID} {...{ zones, control }} />
|
||||||
|
)
|
||||||
|
),
|
||||||
|
[totalErrors, view, control]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ZoneSelect zones={zones} />
|
||||||
|
{resources}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional configuration about VDC.
|
||||||
|
*
|
||||||
|
* @param {Array[object]} zones - Zones available
|
||||||
|
* @returns {object} Optional configuration step
|
||||||
|
*/
|
||||||
|
const Resources = (zones = []) => ({
|
||||||
|
id: STEP_ID,
|
||||||
|
label: T.Resources,
|
||||||
|
resolver: (formData) => SCHEMA(zones),
|
||||||
|
optionsValidate: { abortEarly: false },
|
||||||
|
content: () => Content(zones),
|
||||||
|
})
|
||||||
|
|
||||||
|
Content.propTypes = {
|
||||||
|
data: PropTypes.any,
|
||||||
|
setFormData: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Resources
|
@ -0,0 +1,34 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { object, ObjectSchema } from 'yup'
|
||||||
|
|
||||||
|
import { SCHEMA as CLUSTER_SCHEMA } from './ClustersTable/schema'
|
||||||
|
import { SCHEMA as DATASTORE_SCHEMA } from './DatastoresTable/schema'
|
||||||
|
import { SCHEMA as HOST_SCHEMA } from './HostsTable/schema'
|
||||||
|
import { SCHEMA as NETWORK_SCHEMA } from './VnetsTable/schema'
|
||||||
|
import { SCHEMA as ZONES_SCHEMA } from './ZonesSelect/schema'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array[Object]} zones - Zones objects
|
||||||
|
* @returns {ObjectSchema} Extra configuration schema
|
||||||
|
*/
|
||||||
|
export const SCHEMA = (zones = []) =>
|
||||||
|
object()
|
||||||
|
.concat(ZONES_SCHEMA(zones))
|
||||||
|
.concat(CLUSTER_SCHEMA(zones))
|
||||||
|
.concat(DATASTORE_SCHEMA(zones))
|
||||||
|
.concat(HOST_SCHEMA(zones))
|
||||||
|
.concat(NETWORK_SCHEMA(zones))
|
@ -0,0 +1,141 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { jsonToXml } from 'client/models/Helper'
|
||||||
|
import { createSteps, getUnknownAttributes } from 'client/utils'
|
||||||
|
import CustomAttributes, { STEP_ID as CUSTOM_ID } from './CustomVariables'
|
||||||
|
import General, { STEP_ID as GENERAL_ID } from './General'
|
||||||
|
import Groups, { STEP_ID as GROUPS_ID } from './GroupsTable'
|
||||||
|
import Resources, { STEP_ID as RESOURCES_ID } from './Resources'
|
||||||
|
|
||||||
|
const parseResources = ({ resources, key, newKey }) => {
|
||||||
|
const regex = new RegExp(`^${key}_Z\\d+$`)
|
||||||
|
|
||||||
|
return Object.keys(resources)
|
||||||
|
.filter((internalKey) => regex.test(internalKey))
|
||||||
|
.map((internalKey) => ({
|
||||||
|
zone_id: internalKey.match(/\d+$/)[0],
|
||||||
|
[newKey]: resources[internalKey],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseInitialResources = (resource = [], filterBy, key) => {
|
||||||
|
const data = Array.isArray(resource) ? resource : [resource]
|
||||||
|
|
||||||
|
return data.reduce((acc, obj) => {
|
||||||
|
const zoneId = obj[filterBy]
|
||||||
|
const name = `${key}_Z${zoneId}`
|
||||||
|
|
||||||
|
if (!acc[name]) {
|
||||||
|
acc[name] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[name].push(obj[`${key}_ID`])
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const Steps = createSteps([General, Groups, Resources, CustomAttributes], {
|
||||||
|
transformInitialValue: (vdcTemplate, schema) => {
|
||||||
|
const groups = vdcTemplate?.GROUPS?.ID
|
||||||
|
const groupsData = groups ? (Array.isArray(groups) ? groups : [groups]) : []
|
||||||
|
|
||||||
|
const datastores = parseInitialResources(
|
||||||
|
vdcTemplate?.DATASTORES?.DATASTORE,
|
||||||
|
'ZONE_ID',
|
||||||
|
'DATASTORE'
|
||||||
|
)
|
||||||
|
|
||||||
|
const knownTemplate = schema.cast(
|
||||||
|
{
|
||||||
|
[GENERAL_ID]: { ...vdcTemplate, ...vdcTemplate.TEMPLATE },
|
||||||
|
[GROUPS_ID]: groupsData,
|
||||||
|
[RESOURCES_ID]: {
|
||||||
|
...parseInitialResources(
|
||||||
|
vdcTemplate?.CLUSTERS?.CLUSTER,
|
||||||
|
'ZONE_ID',
|
||||||
|
'CLUSTER'
|
||||||
|
),
|
||||||
|
...datastores,
|
||||||
|
...parseInitialResources(vdcTemplate?.HOSTS?.HOST, 'ZONE_ID', 'HOST'),
|
||||||
|
...parseInitialResources(vdcTemplate?.VNETS?.VNET, 'ZONE_ID', 'VNET'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stripUnknown: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const knownAttributes = {
|
||||||
|
...knownTemplate[GENERAL_ID],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the unknown attributes to the custom variables section
|
||||||
|
knownTemplate[CUSTOM_ID] = getUnknownAttributes(
|
||||||
|
vdcTemplate?.TEMPLATE,
|
||||||
|
knownAttributes
|
||||||
|
)
|
||||||
|
|
||||||
|
return knownTemplate
|
||||||
|
},
|
||||||
|
transformBeforeSubmit: (formData) => {
|
||||||
|
const {
|
||||||
|
[GENERAL_ID]: general = {},
|
||||||
|
[CUSTOM_ID]: customAttributes = {},
|
||||||
|
[GROUPS_ID]: groups = [],
|
||||||
|
[RESOURCES_ID]: resources = {},
|
||||||
|
} = formData ?? {}
|
||||||
|
|
||||||
|
const rtn = {
|
||||||
|
template: jsonToXml({
|
||||||
|
...general,
|
||||||
|
...customAttributes,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources?.ZONE_ID) {
|
||||||
|
rtn.groups = groups
|
||||||
|
|
||||||
|
rtn.clusters = parseResources({
|
||||||
|
resources,
|
||||||
|
key: 'CLUSTER',
|
||||||
|
newKey: 'cluster_id',
|
||||||
|
})
|
||||||
|
|
||||||
|
rtn.datastores = parseResources({
|
||||||
|
resources,
|
||||||
|
key: 'DATASTORE',
|
||||||
|
newKey: 'ds_id',
|
||||||
|
})
|
||||||
|
|
||||||
|
rtn.hosts = parseResources({
|
||||||
|
resources,
|
||||||
|
key: 'HOST',
|
||||||
|
newKey: 'host_id',
|
||||||
|
})
|
||||||
|
|
||||||
|
rtn.vnets = parseResources({
|
||||||
|
resources,
|
||||||
|
key: 'VNET',
|
||||||
|
newKey: 'vnet_id',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Steps
|
@ -0,0 +1,16 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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. *
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
export { default } from './Steps'
|
27
src/fireedge/src/client/components/Forms/Vdc/index.js
Normal file
27
src/fireedge/src/client/components/Forms/Vdc/index.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import { AsyncLoadForm, ConfigurationProps } from 'client/components/HOC'
|
||||||
|
import { CreateStepsCallback } from 'client/utils/schema'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ConfigurationProps} configProps - Configuration
|
||||||
|
* @returns {ReactElement|CreateStepsCallback} Asynchronous loaded form
|
||||||
|
*/
|
||||||
|
const CreateForm = (configProps) =>
|
||||||
|
AsyncLoadForm({ formPath: 'Vdc/CreateForm' }, configProps)
|
||||||
|
|
||||||
|
export { CreateForm }
|
@ -35,6 +35,8 @@ const ClustersTable = (props) => {
|
|||||||
searchProps = {},
|
searchProps = {},
|
||||||
useQuery = useGetClustersQuery,
|
useQuery = useGetClustersQuery,
|
||||||
datastoreId,
|
datastoreId,
|
||||||
|
vdcClusters,
|
||||||
|
zoneId,
|
||||||
...rest
|
...rest
|
||||||
} = props ?? {}
|
} = props ?? {}
|
||||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
@ -45,18 +47,23 @@ const ClustersTable = (props) => {
|
|||||||
data = [],
|
data = [],
|
||||||
isFetching,
|
isFetching,
|
||||||
refetch,
|
refetch,
|
||||||
} = useQuery(undefined, {
|
} = useQuery(
|
||||||
selectFromResult: (result) => ({
|
{ zone: zoneId },
|
||||||
...result,
|
{
|
||||||
data: result?.data?.filter((cluster) => {
|
selectFromResult: (result) => ({
|
||||||
if (datastoreId) {
|
...result,
|
||||||
return cluster?.DATASTORES?.ID?.includes(datastoreId)
|
data: result?.data?.filter((cluster) => {
|
||||||
}
|
if (datastoreId) {
|
||||||
|
return cluster?.DATASTORES?.ID?.includes(datastoreId)
|
||||||
|
} else if (vdcClusters) {
|
||||||
|
return vdcClusters.includes(cluster.ID)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -13,15 +13,20 @@
|
|||||||
* See the License for the specific language governing permissions and *
|
* See the License for the specific language governing permissions and *
|
||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
import { useMemo, ReactElement } from 'react'
|
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useViews } from 'client/features/Auth'
|
import { useViews } from 'client/features/Auth'
|
||||||
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
|
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
|
||||||
|
|
||||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
|
||||||
import DatastoreColumns from 'client/components/Tables/Datastores/columns'
|
import DatastoreColumns from 'client/components/Tables/Datastores/columns'
|
||||||
import DatastoreRow from 'client/components/Tables/Datastores/row'
|
import DatastoreRow from 'client/components/Tables/Datastores/row'
|
||||||
|
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||||
|
import {
|
||||||
|
areArraysEqual,
|
||||||
|
sortStateTables,
|
||||||
|
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
|
||||||
import { RESOURCE_NAMES } from 'client/constants'
|
import { RESOURCE_NAMES } from 'client/constants'
|
||||||
|
import { useFormContext } from 'react-hook-form'
|
||||||
|
|
||||||
const DEFAULT_DATA_CY = 'datastores'
|
const DEFAULT_DATA_CY = 'datastores'
|
||||||
|
|
||||||
@ -33,17 +38,66 @@ const DatastoresTable = (props) => {
|
|||||||
const {
|
const {
|
||||||
rootProps = {},
|
rootProps = {},
|
||||||
searchProps = {},
|
searchProps = {},
|
||||||
filter = (dataToFilter) => dataToFilter,
|
useQuery = useGetDatastoresQuery,
|
||||||
|
vdcDatastores,
|
||||||
|
zoneId,
|
||||||
|
dependOf,
|
||||||
|
filter,
|
||||||
|
reSelectRows,
|
||||||
|
value,
|
||||||
...rest
|
...rest
|
||||||
} = props ?? {}
|
} = props ?? {}
|
||||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||||
|
|
||||||
const { view, getResourceView } = useViews()
|
const { view, getResourceView } = useViews()
|
||||||
const { data = [], isFetching, refetch } = useGetDatastoresQuery()
|
|
||||||
|
|
||||||
// Filter data with input funcion called "filter"
|
let values
|
||||||
const filteredData = filter(data)
|
|
||||||
|
if (typeof filter === 'function') {
|
||||||
|
const { watch } = useFormContext()
|
||||||
|
|
||||||
|
const getDataForDepend = useCallback(
|
||||||
|
(n) => {
|
||||||
|
let dependName = n
|
||||||
|
// removes character '$'
|
||||||
|
if (n.startsWith('$')) dependName = n.slice(1)
|
||||||
|
|
||||||
|
return watch(dependName)
|
||||||
|
},
|
||||||
|
[dependOf]
|
||||||
|
)
|
||||||
|
|
||||||
|
const valuesOfDependField = () => {
|
||||||
|
if (!dependOf) return null
|
||||||
|
|
||||||
|
return Array.isArray(dependOf)
|
||||||
|
? dependOf.map(getDataForDepend)
|
||||||
|
: getDataForDepend(dependOf)
|
||||||
|
}
|
||||||
|
values = valuesOfDependField()
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data = [],
|
||||||
|
isFetching,
|
||||||
|
refetch,
|
||||||
|
} = useQuery(
|
||||||
|
{ zone: zoneId },
|
||||||
|
{
|
||||||
|
selectFromResult: (result) => {
|
||||||
|
const rtn = { ...result }
|
||||||
|
if (vdcDatastores) {
|
||||||
|
const dataRequest = result.data ?? []
|
||||||
|
rtn.data = dataRequest.filter((ds) => vdcDatastores.includes(ds?.ID))
|
||||||
|
} else if (typeof filter === 'function') {
|
||||||
|
rtn.data = filter(result.data ?? [], values ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -54,16 +108,44 @@ const DatastoresTable = (props) => {
|
|||||||
[view]
|
[view]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [stateData, setStateData] = useState(data)
|
||||||
|
|
||||||
|
const updateSelectedRows = () => {
|
||||||
|
if (Array.isArray(values) && typeof reSelectRows === 'function') {
|
||||||
|
const datastores = data
|
||||||
|
.filter((dataObject) => value.includes(dataObject.ID))
|
||||||
|
.map((dataObject) => dataObject.ID)
|
||||||
|
|
||||||
|
const sortedDatastores = sortStateTables(datastores)
|
||||||
|
const sortedValue = sortStateTables(value)
|
||||||
|
if (!areArraysEqual(sortedValue, sortedDatastores)) {
|
||||||
|
reSelectRows(sortedDatastores)
|
||||||
|
setStateData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSelectedRows()
|
||||||
|
}, [dependOf])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (JSON.stringify(data) !== JSON.stringify(stateData)) {
|
||||||
|
updateSelectedRows()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedTable
|
<EnhancedTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={useMemo(() => filteredData, [filteredData])}
|
data={useMemo(() => data, [data])}
|
||||||
rootProps={rootProps}
|
rootProps={rootProps}
|
||||||
searchProps={searchProps}
|
searchProps={searchProps}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
isLoading={isFetching}
|
isLoading={isFetching}
|
||||||
getRowId={(row) => String(row.ID)}
|
getRowId={(row) => String(row.ID)}
|
||||||
RowComponent={DatastoreRow}
|
RowComponent={DatastoreRow}
|
||||||
|
dataDepend={values}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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. *
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorter data.
|
||||||
|
*
|
||||||
|
* @param {Array | any} value - data to order.
|
||||||
|
* @returns {Array|any} data sorted.
|
||||||
|
*/
|
||||||
|
export const sortStateTables = (value) => {
|
||||||
|
if (!Array.isArray(value)) return value
|
||||||
|
|
||||||
|
return value.sort((a, b) => parseInt(a, 10) - parseInt(b, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the arrays are equals.
|
||||||
|
*
|
||||||
|
* @param {Array} array1 - array 1.
|
||||||
|
* @param {Array} array2 - array 2.
|
||||||
|
* @returns {boolean} areEquals.
|
||||||
|
*/
|
||||||
|
export const areArraysEqual = (array1 = [], array2 = []) => {
|
||||||
|
if (array1.length !== array2.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return array1.every((element, index) => element === array2[index])
|
||||||
|
}
|
@ -76,7 +76,7 @@ const GlobalActions = ({
|
|||||||
{!singleSelect && !disableRowSelect && (
|
{!singleSelect && !disableRowSelect && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
{...getToggleAllPageRowsSelectedProps()}
|
{...getToggleAllPageRowsSelectedProps()}
|
||||||
title={Tr(T.ToggleAllCurrentPageRowsSelected)}
|
title={Tr(T.ToggleAllSelectedCardsCurrentPage)}
|
||||||
indeterminate={getToggleAllRowsSelectedProps().indeterminate}
|
indeterminate={getToggleAllRowsSelectedProps().indeterminate}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
/>
|
/>
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
* See the License for the specific language governing permissions and *
|
* See the License for the specific language governing permissions and *
|
||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
import { JSXElementConstructor } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { JSXElementConstructor } from 'react'
|
||||||
|
|
||||||
import { TableProps, Row } from 'react-table'
|
import { Alert, Button, Chip, alertClasses, styled } from '@mui/material'
|
||||||
import { styled, Chip, Alert, Button, alertClasses } from '@mui/material'
|
import { Row, TableProps } from 'react-table'
|
||||||
|
|
||||||
import { Translate } from 'client/components/HOC'
|
import { Translate } from 'client/components/HOC'
|
||||||
import { T } from 'client/constants'
|
import { T } from 'client/constants'
|
||||||
@ -53,7 +53,6 @@ const GlobalSelectedRows = ({
|
|||||||
toggleAllRowsSelected,
|
toggleAllRowsSelected,
|
||||||
state: { selectedRowIds },
|
state: { selectedRowIds },
|
||||||
} = useTableProps
|
} = useTableProps
|
||||||
|
|
||||||
const selectedRows = preFilteredRows.filter((row) => !!selectedRowIds[row.id])
|
const selectedRows = preFilteredRows.filter((row) => !!selectedRowIds[row.id])
|
||||||
const numberOfRowSelected = selectedRows.length
|
const numberOfRowSelected = selectedRows.length
|
||||||
const allSelected = numberOfRowSelected === preFilteredRows.length
|
const allSelected = numberOfRowSelected === preFilteredRows.length
|
||||||
@ -88,6 +87,7 @@ const GlobalSelectedRows = ({
|
|||||||
key={row.id}
|
key={row.id}
|
||||||
label={row.original?.NAME ?? row.id}
|
label={row.original?.NAME ?? row.id}
|
||||||
onDelete={() => row.toggleRowSelected(false)}
|
onDelete={() => row.toggleRowSelected(false)}
|
||||||
|
data-cy="itemSelected"
|
||||||
{...(gotoRowPage && { onClick: () => gotoRowPage(row) })}
|
{...(gotoRowPage && { onClick: () => gotoRowPage(row) })}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
/* eslint-disable jsdoc/require-jsdoc */
|
/* eslint-disable jsdoc/require-jsdoc */
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useMemo } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { Alert, Box, Chip, Grid } from '@mui/material'
|
import { Alert, Box, Chip, Grid } from '@mui/material'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
@ -74,6 +74,7 @@ const EnhancedTable = ({
|
|||||||
searchProps = {},
|
searchProps = {},
|
||||||
noDataMessage,
|
noDataMessage,
|
||||||
messages = [],
|
messages = [],
|
||||||
|
dataDepend,
|
||||||
}) => {
|
}) => {
|
||||||
const styles = EnhancedTableStyles()
|
const styles = EnhancedTableStyles()
|
||||||
|
|
||||||
@ -93,6 +94,20 @@ const EnhancedTable = ({
|
|||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
const stateReducer = (newState, action, prevState) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'RELOAD_STATE': {
|
||||||
|
const updatedState = {
|
||||||
|
...prevState,
|
||||||
|
selectedRowIds: action.value,
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return newState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const useTableProps = useTable(
|
const useTableProps = useTable(
|
||||||
{
|
{
|
||||||
@ -115,6 +130,7 @@ const EnhancedTable = ({
|
|||||||
autoResetGlobalFilter: false,
|
autoResetGlobalFilter: false,
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
initialState: { pageSize, ...initialState },
|
initialState: { pageSize, ...initialState },
|
||||||
|
stateReducer,
|
||||||
},
|
},
|
||||||
useGlobalFilter,
|
useGlobalFilter,
|
||||||
useFilters,
|
useFilters,
|
||||||
@ -137,8 +153,11 @@ const EnhancedTable = ({
|
|||||||
setSortBy,
|
setSortBy,
|
||||||
setGlobalFilter,
|
setGlobalFilter,
|
||||||
state,
|
state,
|
||||||
|
toggleRowSelected: propsToggleRow,
|
||||||
} = useTableProps
|
} = useTableProps
|
||||||
|
|
||||||
|
const [stateData, setStateData] = useState(data)
|
||||||
|
|
||||||
const gotoRowPage = async (row) => {
|
const gotoRowPage = async (row) => {
|
||||||
const pageIdx = Math.floor(row.index / pageSize)
|
const pageIdx = Math.floor(row.index / pageSize)
|
||||||
|
|
||||||
@ -158,6 +177,25 @@ const EnhancedTable = ({
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
}, [state.selectedRowIds])
|
}, [state.selectedRowIds])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
dataDepend &&
|
||||||
|
page.length &&
|
||||||
|
initialState?.selectedRowIds &&
|
||||||
|
JSON.stringify(data) !== JSON.stringify(stateData)
|
||||||
|
) {
|
||||||
|
const initialKeys = Object.keys(initialState.selectedRowIds)
|
||||||
|
page.forEach((row) => {
|
||||||
|
if (!initialKeys.includes(row?.id) && row?.isSelected) {
|
||||||
|
propsToggleRow(row?.id, false)
|
||||||
|
} else if (row?.isSelected) {
|
||||||
|
propsToggleRow(row?.id, false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setStateData(data)
|
||||||
|
}
|
||||||
|
}, [dataDepend])
|
||||||
|
|
||||||
useMountedLayoutEffect(() => {
|
useMountedLayoutEffect(() => {
|
||||||
onSelectedRowsChange?.(
|
onSelectedRowsChange?.(
|
||||||
selectedRows.map((row) => ({ ...row, gotoPage: () => gotoRowPage(row) }))
|
selectedRows.map((row) => ({ ...row, gotoPage: () => gotoRowPage(row) }))
|
||||||
@ -387,6 +425,7 @@ EnhancedTable.propTypes = {
|
|||||||
PropTypes.bool,
|
PropTypes.bool,
|
||||||
]),
|
]),
|
||||||
messages: PropTypes.array,
|
messages: PropTypes.array,
|
||||||
|
dataDepend: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from 'client/components/Tables/Enhanced/Utils'
|
export * from 'client/components/Tables/Enhanced/Utils'
|
||||||
|
@ -30,12 +30,27 @@ const DEFAULT_DATA_CY = 'groups'
|
|||||||
* @returns {ReactElement} Groups table
|
* @returns {ReactElement} Groups table
|
||||||
*/
|
*/
|
||||||
const GroupsTable = (props) => {
|
const GroupsTable = (props) => {
|
||||||
const { rootProps = {}, searchProps = {}, ...rest } = props ?? {}
|
const { rootProps = {}, searchProps = {}, vdcGroups, ...rest } = props ?? {}
|
||||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||||
|
|
||||||
const { view, getResourceView } = useViews()
|
const { view, getResourceView } = useViews()
|
||||||
const { data = [], isFetching, refetch } = useGetGroupsQuery()
|
const {
|
||||||
|
data = [],
|
||||||
|
isFetching,
|
||||||
|
refetch,
|
||||||
|
} = useGetGroupsQuery(undefined, {
|
||||||
|
selectFromResult: (result) => ({
|
||||||
|
...result,
|
||||||
|
data: result?.data?.filter((group) => {
|
||||||
|
if (vdcGroups) {
|
||||||
|
return vdcGroups.includes(group.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -13,15 +13,20 @@
|
|||||||
* See the License for the specific language governing permissions and *
|
* See the License for the specific language governing permissions and *
|
||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
import { useMemo, ReactElement } from 'react'
|
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useViews } from 'client/features/Auth'
|
import { useViews } from 'client/features/Auth'
|
||||||
import { useGetHostsQuery } from 'client/features/OneApi/host'
|
import { useGetHostsQuery } from 'client/features/OneApi/host'
|
||||||
|
|
||||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||||
|
import {
|
||||||
|
areArraysEqual,
|
||||||
|
sortStateTables,
|
||||||
|
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
|
||||||
import HostColumns from 'client/components/Tables/Hosts/columns'
|
import HostColumns from 'client/components/Tables/Hosts/columns'
|
||||||
import HostRow from 'client/components/Tables/Hosts/row'
|
import HostRow from 'client/components/Tables/Hosts/row'
|
||||||
import { RESOURCE_NAMES } from 'client/constants'
|
import { RESOURCE_NAMES } from 'client/constants'
|
||||||
|
import { useFormContext } from 'react-hook-form'
|
||||||
|
|
||||||
const DEFAULT_DATA_CY = 'hosts'
|
const DEFAULT_DATA_CY = 'hosts'
|
||||||
|
|
||||||
@ -34,13 +39,65 @@ const HostsTable = (props) => {
|
|||||||
rootProps = {},
|
rootProps = {},
|
||||||
searchProps = {},
|
searchProps = {},
|
||||||
useQuery = useGetHostsQuery,
|
useQuery = useGetHostsQuery,
|
||||||
|
vdcHosts,
|
||||||
|
zoneId,
|
||||||
|
dependOf,
|
||||||
|
filter,
|
||||||
|
reSelectRows,
|
||||||
|
value,
|
||||||
...rest
|
...rest
|
||||||
} = props ?? {}
|
} = props ?? {}
|
||||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||||
|
|
||||||
const { view, getResourceView } = useViews()
|
const { view, getResourceView } = useViews()
|
||||||
const { data = [], isFetching, refetch } = useQuery()
|
|
||||||
|
let values
|
||||||
|
|
||||||
|
if (typeof filter === 'function') {
|
||||||
|
const { watch } = useFormContext()
|
||||||
|
|
||||||
|
const getDataForDepend = useCallback(
|
||||||
|
(n) => {
|
||||||
|
let dependName = n
|
||||||
|
// removes character '$'
|
||||||
|
if (n.startsWith('$')) dependName = n.slice(1)
|
||||||
|
|
||||||
|
return watch(dependName)
|
||||||
|
},
|
||||||
|
[dependOf]
|
||||||
|
)
|
||||||
|
|
||||||
|
const valuesOfDependField = () => {
|
||||||
|
if (!dependOf) return null
|
||||||
|
|
||||||
|
return Array.isArray(dependOf)
|
||||||
|
? dependOf.map(getDataForDepend)
|
||||||
|
: getDataForDepend(dependOf)
|
||||||
|
}
|
||||||
|
values = valuesOfDependField()
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data = [],
|
||||||
|
isFetching,
|
||||||
|
refetch,
|
||||||
|
} = useQuery(
|
||||||
|
{ zone: zoneId },
|
||||||
|
{
|
||||||
|
selectFromResult: (result) => {
|
||||||
|
const rtn = { ...result }
|
||||||
|
if (vdcHosts) {
|
||||||
|
const dataRequest = result.data ?? []
|
||||||
|
rtn.data = dataRequest.filter((host) => vdcHosts.includes(host?.ID))
|
||||||
|
} else if (typeof filter === 'function') {
|
||||||
|
rtn.data = filter(result.data ?? [], values ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -51,6 +108,33 @@ const HostsTable = (props) => {
|
|||||||
[view]
|
[view]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [stateData, setStateData] = useState(data)
|
||||||
|
|
||||||
|
const updateSelectedRows = () => {
|
||||||
|
if (Array.isArray(values) && typeof reSelectRows === 'function') {
|
||||||
|
const datastores = data
|
||||||
|
.filter((dataObject) => value.includes(dataObject.ID))
|
||||||
|
.map((dataObject) => dataObject.ID)
|
||||||
|
|
||||||
|
const sortedDatastores = sortStateTables(datastores)
|
||||||
|
const sortedValue = sortStateTables(value)
|
||||||
|
if (!areArraysEqual(sortedValue, sortedDatastores)) {
|
||||||
|
reSelectRows(sortedDatastores)
|
||||||
|
setStateData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSelectedRows()
|
||||||
|
}, [dependOf])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (JSON.stringify(data) !== JSON.stringify(stateData)) {
|
||||||
|
updateSelectedRows()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedTable
|
<EnhancedTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@ -61,6 +145,7 @@ const HostsTable = (props) => {
|
|||||||
isLoading={isFetching}
|
isLoading={isFetching}
|
||||||
getRowId={(row) => String(row.ID)}
|
getRowId={(row) => String(row.ID)}
|
||||||
RowComponent={HostRow}
|
RowComponent={HostRow}
|
||||||
|
dataDepend={values}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -222,7 +222,6 @@ const Actions = () => {
|
|||||||
form: RecoverForm,
|
form: RecoverForm,
|
||||||
onSubmit: (rows) => async (formData) => {
|
onSubmit: (rows) => async (formData) => {
|
||||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||||
console.log(`RECOVER ${ids}`, formData)
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
ids.map((id) => recover({ id, ...formData }))
|
ids.map((id) => recover({ id, ...formData }))
|
||||||
)
|
)
|
||||||
|
@ -13,15 +13,20 @@
|
|||||||
* See the License for the specific language governing permissions and *
|
* See the License for the specific language governing permissions and *
|
||||||
* limitations under the License. *
|
* limitations under the License. *
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
import { useMemo, ReactElement } from 'react'
|
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useViews } from 'client/features/Auth'
|
import { useViews } from 'client/features/Auth'
|
||||||
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
|
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
|
||||||
|
|
||||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||||
|
import {
|
||||||
|
areArraysEqual,
|
||||||
|
sortStateTables,
|
||||||
|
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
|
||||||
import VNetworkColumns from 'client/components/Tables/VNetworks/columns'
|
import VNetworkColumns from 'client/components/Tables/VNetworks/columns'
|
||||||
import VNetworkRow from 'client/components/Tables/VNetworks/row'
|
import VNetworkRow from 'client/components/Tables/VNetworks/row'
|
||||||
import { RESOURCE_NAMES } from 'client/constants'
|
import { RESOURCE_NAMES } from 'client/constants'
|
||||||
|
import { useFormContext } from 'react-hook-form'
|
||||||
|
|
||||||
const DEFAULT_DATA_CY = 'vnets'
|
const DEFAULT_DATA_CY = 'vnets'
|
||||||
|
|
||||||
@ -34,13 +39,65 @@ const VNetworksTable = (props) => {
|
|||||||
rootProps = {},
|
rootProps = {},
|
||||||
searchProps = {},
|
searchProps = {},
|
||||||
useQuery = useGetVNetworksQuery,
|
useQuery = useGetVNetworksQuery,
|
||||||
|
vdcVnets,
|
||||||
|
zoneId,
|
||||||
|
dependOf,
|
||||||
|
filter,
|
||||||
|
reSelectRows,
|
||||||
|
value,
|
||||||
...rest
|
...rest
|
||||||
} = props ?? {}
|
} = props ?? {}
|
||||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||||
|
|
||||||
const { view, getResourceView } = useViews()
|
const { view, getResourceView } = useViews()
|
||||||
const { data = [], isFetching, refetch } = useQuery()
|
|
||||||
|
let values
|
||||||
|
|
||||||
|
if (typeof filter === 'function') {
|
||||||
|
const { watch } = useFormContext()
|
||||||
|
|
||||||
|
const getDataForDepend = useCallback(
|
||||||
|
(n) => {
|
||||||
|
let dependName = n
|
||||||
|
// removes character '$'
|
||||||
|
if (n.startsWith('$')) dependName = n.slice(1)
|
||||||
|
|
||||||
|
return watch(dependName)
|
||||||
|
},
|
||||||
|
[dependOf]
|
||||||
|
)
|
||||||
|
|
||||||
|
const valuesOfDependField = () => {
|
||||||
|
if (!dependOf) return null
|
||||||
|
|
||||||
|
return Array.isArray(dependOf)
|
||||||
|
? dependOf.map(getDataForDepend)
|
||||||
|
: getDataForDepend(dependOf)
|
||||||
|
}
|
||||||
|
values = valuesOfDependField()
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data = [],
|
||||||
|
isFetching,
|
||||||
|
refetch,
|
||||||
|
} = useQuery(
|
||||||
|
{ zone: zoneId },
|
||||||
|
{
|
||||||
|
selectFromResult: (result) => {
|
||||||
|
const rtn = { ...result }
|
||||||
|
if (vdcVnets) {
|
||||||
|
const dataRequest = result.data ?? []
|
||||||
|
rtn.data = dataRequest.filter((vnet) => vdcVnets.includes(vnet?.ID))
|
||||||
|
} else if (typeof filter === 'function') {
|
||||||
|
rtn.data = filter(result.data ?? [], values ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -51,6 +108,34 @@ const VNetworksTable = (props) => {
|
|||||||
[view]
|
[view]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [stateData, setStateData] = useState(data)
|
||||||
|
|
||||||
|
const updateSelectedRows = () => {
|
||||||
|
if (Array.isArray(values) && typeof reSelectRows === 'function') {
|
||||||
|
const datastores = data
|
||||||
|
.filter((dataObject) => value.includes(dataObject.ID))
|
||||||
|
.map((dataObject) => dataObject.ID)
|
||||||
|
|
||||||
|
const sortedDatastores = sortStateTables(datastores)
|
||||||
|
const sortedValue = sortStateTables(value)
|
||||||
|
|
||||||
|
if (!areArraysEqual(sortedValue, sortedDatastores)) {
|
||||||
|
reSelectRows(sortedDatastores)
|
||||||
|
setStateData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSelectedRows()
|
||||||
|
}, [dependOf])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (JSON.stringify(data) !== JSON.stringify(stateData)) {
|
||||||
|
updateSelectedRows()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EnhancedTable
|
<EnhancedTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@ -61,6 +146,7 @@ const VNetworksTable = (props) => {
|
|||||||
isLoading={isFetching}
|
isLoading={isFetching}
|
||||||
getRowId={(row) => String(row.ID)}
|
getRowId={(row) => String(row.ID)}
|
||||||
RowComponent={VNetworkRow}
|
RowComponent={VNetworkRow}
|
||||||
|
dataDepend={values}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { Typography } from '@mui/material'
|
||||||
|
import { AddCircledOutline, Trash } from 'iconoir-react'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { useViews } from 'client/features/Auth'
|
||||||
|
import { useRemoveVDCMutation } from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
import {
|
||||||
|
createActions,
|
||||||
|
GlobalAction,
|
||||||
|
} from 'client/components/Tables/Enhanced/Utils'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
import { Translate } from 'client/components/HOC'
|
||||||
|
import { RESOURCE_NAMES, T, VDC_ACTIONS } from 'client/constants'
|
||||||
|
|
||||||
|
const ListVDCNames = ({ rows = [] }) =>
|
||||||
|
rows?.map?.(({ id, original }) => {
|
||||||
|
const { ID, NAME } = original
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography
|
||||||
|
key={`vdc-${id}`}
|
||||||
|
variant="inherit"
|
||||||
|
component="span"
|
||||||
|
display="block"
|
||||||
|
>
|
||||||
|
{`#${ID} ${NAME}`}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const MessageToConfirmAction = (rows, description) => (
|
||||||
|
<>
|
||||||
|
<ListVDCNames rows={rows} />
|
||||||
|
{description && <Translate word={description} />}
|
||||||
|
<Translate word={T.DoYouWantProceed} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the actions to operate resources on VM Template table.
|
||||||
|
*
|
||||||
|
* @returns {GlobalAction} - Actions
|
||||||
|
*/
|
||||||
|
const Actions = () => {
|
||||||
|
const history = useHistory()
|
||||||
|
const { view, getResourceView } = useViews()
|
||||||
|
const [remove] = useRemoveVDCMutation()
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
createActions({
|
||||||
|
filters: getResourceView(RESOURCE_NAMES.VDC)?.actions,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
accessor: VDC_ACTIONS.CREATE_DIALOG,
|
||||||
|
tooltip: T.Create,
|
||||||
|
icon: AddCircledOutline,
|
||||||
|
action: () => history.push(PATH.SYSTEM.VDCS.CREATE),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: VDC_ACTIONS.UPDATE_DIALOG,
|
||||||
|
label: T.Update,
|
||||||
|
tooltip: T.Update,
|
||||||
|
selected: { max: 1 },
|
||||||
|
dataCy: `vdc_${VDC_ACTIONS.UPDATE_DIALOG}`,
|
||||||
|
color: 'secondary',
|
||||||
|
action: (rows) => {
|
||||||
|
const vdcTemplate = rows?.[0]?.original ?? {}
|
||||||
|
history.push(PATH.SYSTEM.VDCS.CREATE, vdcTemplate)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: VDC_ACTIONS.DELETE,
|
||||||
|
tooltip: T.Delete,
|
||||||
|
icon: Trash,
|
||||||
|
color: 'error',
|
||||||
|
selected: { min: 1 },
|
||||||
|
dataCy: `vdc_${VDC_ACTIONS.DELETE}`,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
isConfirmDialog: true,
|
||||||
|
dialogProps: {
|
||||||
|
title: T.Delete,
|
||||||
|
dataCy: `modal-${VDC_ACTIONS.DELETE}`,
|
||||||
|
children: MessageToConfirmAction,
|
||||||
|
},
|
||||||
|
onSubmit: (rows) => async () => {
|
||||||
|
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||||
|
await Promise.all(ids.map((id) => remove({ id })))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[view]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Actions
|
@ -0,0 +1,34 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { Column } from 'react-table'
|
||||||
|
|
||||||
|
import { T } from 'client/constants'
|
||||||
|
|
||||||
|
/** @type {Column[]} VM Template columns */
|
||||||
|
const COLUMNS = [
|
||||||
|
{ Header: T.ID, id: 'id', accessor: 'ID', sortType: 'number' },
|
||||||
|
{ Header: T.Name, id: 'name', accessor: 'NAME' },
|
||||||
|
{
|
||||||
|
Header: T.Label,
|
||||||
|
id: 'label',
|
||||||
|
accessor: 'TEMPLATE.LABELS',
|
||||||
|
filter: 'includesSome',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
COLUMNS.noFilterIds = ['id', 'name', 'label']
|
||||||
|
|
||||||
|
export default COLUMNS
|
@ -0,0 +1,67 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { useMemo, ReactElement } from 'react'
|
||||||
|
|
||||||
|
import { useViews } from 'client/features/Auth'
|
||||||
|
import { useGetVDCsQuery } from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||||
|
import VDCColumns from 'client/components/Tables/VirtualDataCenters/columns'
|
||||||
|
import VDCRow from 'client/components/Tables/VirtualDataCenters/row'
|
||||||
|
import { RESOURCE_NAMES } from 'client/constants'
|
||||||
|
|
||||||
|
const DEFAULT_DATA_CY = 'vdcs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @returns {ReactElement} VM Templates table
|
||||||
|
*/
|
||||||
|
const VDCsTable = (props) => {
|
||||||
|
const { rootProps = {}, searchProps = {}, ...rest } = props ?? {}
|
||||||
|
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||||
|
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||||
|
|
||||||
|
const { view, getResourceView } = useViews()
|
||||||
|
const { data = [], isFetching, refetch } = useGetVDCsQuery()
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() =>
|
||||||
|
createColumns({
|
||||||
|
filters: getResourceView(RESOURCE_NAMES.VDC)?.filters,
|
||||||
|
columns: VDCColumns,
|
||||||
|
}),
|
||||||
|
[view]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EnhancedTable
|
||||||
|
columns={columns}
|
||||||
|
data={useMemo(() => data, [data])}
|
||||||
|
rootProps={rootProps}
|
||||||
|
searchProps={searchProps}
|
||||||
|
refetch={refetch}
|
||||||
|
isLoading={isFetching}
|
||||||
|
getRowId={(row) => String(row.ID)}
|
||||||
|
RowComponent={VDCRow}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
VDCsTable.propTypes = { ...EnhancedTable.propTypes }
|
||||||
|
VDCsTable.displayName = 'VDCsTable'
|
||||||
|
|
||||||
|
export default VDCsTable
|
@ -0,0 +1,69 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { memo, useMemo, useCallback } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import vdcApi, { useUpdateVDCMutation } from 'client/features/OneApi/vdc'
|
||||||
|
import { VirtualDataCenterCard } from 'client/components/Cards'
|
||||||
|
import { jsonToXml } from 'client/models/Helper'
|
||||||
|
|
||||||
|
const Row = memo(
|
||||||
|
({ original, value, onClickLabel, ...props }) => {
|
||||||
|
const [update] = useUpdateVDCMutation()
|
||||||
|
|
||||||
|
const state = vdcApi.endpoints.getVDCs.useQueryState(undefined, {
|
||||||
|
selectFromResult: ({ data = [] }) =>
|
||||||
|
data.find((vdc) => +vdc.ID === +original.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
const memoVdc = useMemo(() => state ?? original, [state, original])
|
||||||
|
|
||||||
|
const handleDeleteLabel = useCallback(
|
||||||
|
(label) => {
|
||||||
|
const currentLabels = memoVdc.TEMPLATE?.LABELS?.split(',')
|
||||||
|
const newLabels = currentLabels.filter((l) => l !== label).join(',')
|
||||||
|
const newUserTemplate = { ...memoVdc.TEMPLATE, LABELS: newLabels }
|
||||||
|
const templateXml = jsonToXml(newUserTemplate)
|
||||||
|
|
||||||
|
update({ id: original.ID, template: templateXml, replace: 0 })
|
||||||
|
},
|
||||||
|
[memoVdc.TEMPLATE?.LABELS, update]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtualDataCenterCard
|
||||||
|
template={memoVdc}
|
||||||
|
rootProps={props}
|
||||||
|
onClickLabel={onClickLabel}
|
||||||
|
onDeleteLabel={handleDeleteLabel}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(prev, next) => prev.className === next.className
|
||||||
|
)
|
||||||
|
|
||||||
|
Row.propTypes = {
|
||||||
|
original: PropTypes.object,
|
||||||
|
value: PropTypes.object,
|
||||||
|
isSelected: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
onClickLabel: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
Row.displayName = 'VirtualDataCenterRow'
|
||||||
|
|
||||||
|
export default Row
|
@ -38,6 +38,7 @@ import VNetworksTable from 'client/components/Tables/VNetworks'
|
|||||||
import VNetworkTemplatesTable from 'client/components/Tables/VNetworkTemplates'
|
import VNetworkTemplatesTable from 'client/components/Tables/VNetworkTemplates'
|
||||||
import VRoutersTable from 'client/components/Tables/VRouters'
|
import VRoutersTable from 'client/components/Tables/VRouters'
|
||||||
import ZonesTable from 'client/components/Tables/Zones'
|
import ZonesTable from 'client/components/Tables/Zones'
|
||||||
|
import VDCsTable from 'client/components/Tables/VirtualDataCenters'
|
||||||
|
|
||||||
export * from 'client/components/Tables/Enhanced/Utils'
|
export * from 'client/components/Tables/Enhanced/Utils'
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ export {
|
|||||||
ServicesTable,
|
ServicesTable,
|
||||||
ServiceTemplatesTable,
|
ServiceTemplatesTable,
|
||||||
UsersTable,
|
UsersTable,
|
||||||
|
VDCsTable,
|
||||||
VmsTable,
|
VmsTable,
|
||||||
VmTemplatesTable,
|
VmTemplatesTable,
|
||||||
VNetworksTable,
|
VNetworksTable,
|
||||||
|
80
src/fireedge/src/client/components/Tabs/Vdc/Clusters.js
Normal file
80
src/fireedge/src/client/components/Tabs/Vdc/Clusters.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 PropTypes from 'prop-types'
|
||||||
|
import { ReactElement, useState } from 'react'
|
||||||
|
import { generatePath, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
|
||||||
|
import { ClustersTable } from 'client/components/Tables'
|
||||||
|
import SelectZones from 'client/components/Tabs/Vdc/SelecZones'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {string} props.id - Datastore id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const ClustersInfoTab = ({ id }) => {
|
||||||
|
const [selectedZone, setSelectedZone] = useState('0')
|
||||||
|
const path = PATH.INFRASTRUCTURE.CLUSTERS.DETAIL
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const handleRowClick = (rowId) => {
|
||||||
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = useGetVDCQuery({ id })
|
||||||
|
const { data: dataZones } = useGetZonesQuery()
|
||||||
|
|
||||||
|
const vdcData = data.CLUSTERS.CLUSTER
|
||||||
|
? Array.isArray(data.CLUSTERS.CLUSTER)
|
||||||
|
? data.CLUSTERS.CLUSTER
|
||||||
|
: [data.CLUSTERS.CLUSTER]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const vdcClusterIds = vdcData
|
||||||
|
.filter((ds) => ds.ZONE_ID === selectedZone)
|
||||||
|
.map((ds) => ds.CLUSTER_ID)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectZones
|
||||||
|
data={dataZones}
|
||||||
|
handleZone={setSelectedZone}
|
||||||
|
value={selectedZone}
|
||||||
|
/>
|
||||||
|
<ClustersTable
|
||||||
|
disableRowSelect
|
||||||
|
disableGlobalSort
|
||||||
|
vdcClusters={vdcClusterIds}
|
||||||
|
onRowClick={(row) => handleRowClick(row.ID)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ClustersInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
ClustersInfoTab.displayName = 'ClustersInfoTab'
|
||||||
|
|
||||||
|
export default ClustersInfoTab
|
81
src/fireedge/src/client/components/Tabs/Vdc/Datastores.js
Normal file
81
src/fireedge/src/client/components/Tabs/Vdc/Datastores.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement, useState } from 'react'
|
||||||
|
import { generatePath, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
|
||||||
|
import { DatastoresTable } from 'client/components/Tables'
|
||||||
|
import SelectZones from 'client/components/Tabs/Vdc/SelecZones'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {string} props.id - Datastore id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const DatastoresInfoTab = ({ id }) => {
|
||||||
|
const [selectedZone, setSelectedZone] = useState('0')
|
||||||
|
|
||||||
|
const path = PATH.STORAGE.DATASTORES.DETAIL
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const handleRowClick = (rowId) => {
|
||||||
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = useGetVDCQuery({ id })
|
||||||
|
const { data: dataZones } = useGetZonesQuery()
|
||||||
|
|
||||||
|
const vdcData = data.DATASTORES.DATASTORE
|
||||||
|
? Array.isArray(data.DATASTORES.DATASTORE)
|
||||||
|
? data.DATASTORES.DATASTORE
|
||||||
|
: [data.DATASTORES.DATASTORE]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const vdcDatastoresIds = vdcData
|
||||||
|
.filter((ds) => ds.ZONE_ID === selectedZone)
|
||||||
|
.map((ds) => ds.DATASTORE_ID)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectZones
|
||||||
|
data={dataZones}
|
||||||
|
handleZone={setSelectedZone}
|
||||||
|
value={selectedZone}
|
||||||
|
/>
|
||||||
|
<DatastoresTable
|
||||||
|
disableRowSelect
|
||||||
|
disableGlobalSort
|
||||||
|
vdcDatastores={vdcDatastoresIds}
|
||||||
|
onRowClick={(row) => handleRowClick(row.ID)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DatastoresInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
DatastoresInfoTab.displayName = 'DatastoresInfoTab'
|
||||||
|
|
||||||
|
export default DatastoresInfoTab
|
60
src/fireedge/src/client/components/Tabs/Vdc/Groups.js
Normal file
60
src/fireedge/src/client/components/Tabs/Vdc/Groups.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { useHistory, generatePath } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
|
||||||
|
import { GroupsTable } from 'client/components/Tables'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {string} props.id - Datastore id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const GroupsInfoTab = ({ id }) => {
|
||||||
|
const path = PATH.SYSTEM.GROUPS.DETAIL
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const handleRowClick = (rowId) => {
|
||||||
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = useGetVDCQuery({ id })
|
||||||
|
const vdcGroups = data.GROUPS.ID ?? []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GroupsTable
|
||||||
|
disableRowSelect
|
||||||
|
disableGlobalSort
|
||||||
|
vdcGroups={vdcGroups}
|
||||||
|
onRowClick={(row) => handleRowClick(row.ID)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsInfoTab.displayName = 'GroupsInfoTab'
|
||||||
|
|
||||||
|
export default GroupsInfoTab
|
80
src/fireedge/src/client/components/Tabs/Vdc/Hosts.js
Normal file
80
src/fireedge/src/client/components/Tabs/Vdc/Hosts.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 PropTypes from 'prop-types'
|
||||||
|
import { ReactElement, useState } from 'react'
|
||||||
|
import { generatePath, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
|
||||||
|
import { HostsTable } from 'client/components/Tables'
|
||||||
|
import SelectZones from 'client/components/Tabs/Vdc/SelecZones'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {string} props.id - Datastore id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const HostsInfoTab = ({ id }) => {
|
||||||
|
const [selectedZone, setSelectedZone] = useState('0')
|
||||||
|
const path = PATH.INFRASTRUCTURE.HOSTS.DETAIL
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const handleRowClick = (rowId) => {
|
||||||
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = useGetVDCQuery({ id })
|
||||||
|
const { data: dataZones } = useGetZonesQuery()
|
||||||
|
|
||||||
|
const vdcData = data.HOSTS.HOST
|
||||||
|
? Array.isArray(data.HOSTS.HOST)
|
||||||
|
? data.HOSTS.HOST
|
||||||
|
: [data.HOSTS.HOST]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const vdcHostsIds = vdcData
|
||||||
|
.filter((ds) => ds.ZONE_ID === selectedZone)
|
||||||
|
.map((ds) => ds.HOST_ID)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectZones
|
||||||
|
data={dataZones}
|
||||||
|
handleZone={setSelectedZone}
|
||||||
|
value={selectedZone}
|
||||||
|
/>
|
||||||
|
<HostsTable
|
||||||
|
disableRowSelect
|
||||||
|
disableGlobalSort
|
||||||
|
vdcHosts={vdcHostsIds}
|
||||||
|
onRowClick={(row) => handleRowClick(row.ID)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HostsInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
HostsInfoTab.displayName = 'HostsInfoTab'
|
||||||
|
|
||||||
|
export default HostsInfoTab
|
99
src/fireedge/src/client/components/Tabs/Vdc/Info/index.js
Normal file
99
src/fireedge/src/client/components/Tabs/Vdc/Info/index.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Stack } from '@mui/material'
|
||||||
|
|
||||||
|
import {
|
||||||
|
useGetVDCQuery,
|
||||||
|
useUpdateVDCMutation,
|
||||||
|
} from 'client/features/OneApi/vdc'
|
||||||
|
import Information from 'client/components/Tabs/Vdc/Info/information'
|
||||||
|
import {
|
||||||
|
filterAttributes,
|
||||||
|
getActionsAvailable,
|
||||||
|
jsonToXml,
|
||||||
|
} from 'client/models/Helper'
|
||||||
|
import { AttributePanel } from 'client/components/Tabs/Common'
|
||||||
|
import { Tr } from 'client/components/HOC'
|
||||||
|
import { T } from 'client/constants'
|
||||||
|
import { cloneObject, set } from 'client/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {object} props.tabProps - Tab information
|
||||||
|
* @param {string} props.id - Template id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const VDCInfoTab = ({ tabProps = {}, id }) => {
|
||||||
|
const {
|
||||||
|
information_panel: informationPanel,
|
||||||
|
attributes_panel: attributesPanel,
|
||||||
|
} = tabProps
|
||||||
|
|
||||||
|
const { data: vdc = {} } = useGetVDCQuery({ id })
|
||||||
|
const { TEMPLATE } = vdc
|
||||||
|
const [updateTemplate] = useUpdateVDCMutation()
|
||||||
|
|
||||||
|
const getActions = (actions) => getActionsAvailable(actions)
|
||||||
|
|
||||||
|
const { attributes } = filterAttributes(TEMPLATE)
|
||||||
|
|
||||||
|
const handleAttributeInXml = async (path, newValue) => {
|
||||||
|
const newTemplate = cloneObject(TEMPLATE)
|
||||||
|
set(newTemplate, path, newValue)
|
||||||
|
|
||||||
|
const xml = jsonToXml(newTemplate)
|
||||||
|
await updateTemplate({ id, template: xml, replace: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
display="grid"
|
||||||
|
gap="1em"
|
||||||
|
gridTemplateColumns="repeat(auto-fit, minmax(49%, 1fr))"
|
||||||
|
padding={{ sm: '0.8em' }}
|
||||||
|
>
|
||||||
|
{informationPanel?.enabled && (
|
||||||
|
<Information
|
||||||
|
actions={getActions(informationPanel?.actions)}
|
||||||
|
vdc={vdc}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{attributesPanel?.enabled && (
|
||||||
|
<AttributePanel
|
||||||
|
attributes={attributes}
|
||||||
|
actions={getActions(attributesPanel?.actions)}
|
||||||
|
title={Tr(T.Attributes)}
|
||||||
|
handleAdd={handleAttributeInXml}
|
||||||
|
handleEdit={handleAttributeInXml}
|
||||||
|
handleDelete={handleAttributeInXml}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
VDCInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
VDCInfoTab.displayName = 'VDCInfoTab'
|
||||||
|
|
||||||
|
export default VDCInfoTab
|
@ -0,0 +1,68 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import { List } from 'client/components/Tabs/Common'
|
||||||
|
import { useRenameVDCMutation } from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
import { T, VDC_ACTIONS } from 'client/constants'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {object} props.vdc - Template
|
||||||
|
* @param {string[]} props.actions - Available actions to information tab
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const InformationPanel = ({ vdc = {}, actions }) => {
|
||||||
|
const [rename] = useRenameVDCMutation()
|
||||||
|
|
||||||
|
const { ID, NAME } = vdc
|
||||||
|
|
||||||
|
const handleRename = async (_, newName) => {
|
||||||
|
await rename({ id: ID, name: newName })
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = [
|
||||||
|
{ name: T.ID, value: ID, dataCy: 'id' },
|
||||||
|
{
|
||||||
|
name: T.Name,
|
||||||
|
value: NAME,
|
||||||
|
canEdit: actions?.includes?.(VDC_ACTIONS.RENAME),
|
||||||
|
handleEdit: handleRename,
|
||||||
|
dataCy: 'name',
|
||||||
|
},
|
||||||
|
].filter(Boolean)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
title={T.Information}
|
||||||
|
list={info}
|
||||||
|
containerProps={{ sx: { gridRow: 'span 3' } }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
InformationPanel.displayName = 'InformationPanel'
|
||||||
|
|
||||||
|
InformationPanel.propTypes = {
|
||||||
|
actions: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
vdc: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InformationPanel
|
51
src/fireedge/src/client/components/Tabs/Vdc/SelecZones.js
Normal file
51
src/fireedge/src/client/components/Tabs/Vdc/SelecZones.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { MenuItem, Select } from '@mui/material'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
/**
|
||||||
|
* Render zone selector.
|
||||||
|
*
|
||||||
|
* @param {object} props - props
|
||||||
|
* @param {Array} props.data - data for selector
|
||||||
|
* @param {Function} props.handleZone - selector function
|
||||||
|
* @param {string} props.value - value
|
||||||
|
* @returns {ReactElement} - selector zones
|
||||||
|
*/
|
||||||
|
const SelectZones = ({ data = [], handleZone, value }) => {
|
||||||
|
const handleChange = (event) => {
|
||||||
|
handleZone(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select value={value} onChange={handleChange}>
|
||||||
|
{data.map(({ ID, NAME }) => (
|
||||||
|
<MenuItem key={ID} value={ID}>
|
||||||
|
{NAME}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SelectZones.propTypes = {
|
||||||
|
data: PropTypes.array,
|
||||||
|
handleZone: PropTypes.func,
|
||||||
|
value: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectZones.displayName = 'SelectZones'
|
||||||
|
|
||||||
|
export default SelectZones
|
80
src/fireedge/src/client/components/Tabs/Vdc/Vnets.js
Normal file
80
src/fireedge/src/client/components/Tabs/Vdc/Vnets.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 PropTypes from 'prop-types'
|
||||||
|
import { ReactElement, useState } from 'react'
|
||||||
|
import { generatePath, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
|
||||||
|
import { VNetworksTable } from 'client/components/Tables'
|
||||||
|
import SelectZones from 'client/components/Tabs/Vdc/SelecZones'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders mainly information tab.
|
||||||
|
*
|
||||||
|
* @param {object} props - Props
|
||||||
|
* @param {string} props.id - Datastore id
|
||||||
|
* @returns {ReactElement} Information tab
|
||||||
|
*/
|
||||||
|
const VnetsInfoTab = ({ id }) => {
|
||||||
|
const [selectedZone, setSelectedZone] = useState('0')
|
||||||
|
const path = PATH.NETWORK.VNETS.DETAIL
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const handleRowClick = (rowId) => {
|
||||||
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = useGetVDCQuery({ id })
|
||||||
|
const { data: dataZones } = useGetZonesQuery()
|
||||||
|
|
||||||
|
const vdcData = data.VNETS.VNET
|
||||||
|
? Array.isArray(data.VNETS.VNET)
|
||||||
|
? data.VNETS.VNET
|
||||||
|
: [data.VNETS.VNET]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const vdcVnetsIds = vdcData
|
||||||
|
.filter((ds) => ds.ZONE_ID === selectedZone)
|
||||||
|
.map((ds) => ds.VNET_ID)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectZones
|
||||||
|
data={dataZones}
|
||||||
|
handleZone={setSelectedZone}
|
||||||
|
value={selectedZone}
|
||||||
|
/>
|
||||||
|
<VNetworksTable
|
||||||
|
disableRowSelect
|
||||||
|
disableGlobalSort
|
||||||
|
vdcVnets={vdcVnetsIds}
|
||||||
|
onRowClick={(row) => handleRowClick(row.ID)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
VnetsInfoTab.propTypes = {
|
||||||
|
tabProps: PropTypes.object,
|
||||||
|
id: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
VnetsInfoTab.displayName = 'VnetsInfoTab'
|
||||||
|
|
||||||
|
export default VnetsInfoTab
|
71
src/fireedge/src/client/components/Tabs/Vdc/index.js
Normal file
71
src/fireedge/src/client/components/Tabs/Vdc/index.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { Alert, LinearProgress } from '@mui/material'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { memo, useMemo } from 'react'
|
||||||
|
|
||||||
|
import { RESOURCE_NAMES } from 'client/constants'
|
||||||
|
import { useViews } from 'client/features/Auth'
|
||||||
|
import { useGetVDCQuery } from 'client/features/OneApi/vdc'
|
||||||
|
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||||
|
|
||||||
|
import Tabs from 'client/components/Tabs'
|
||||||
|
import Datastores from 'client/components/Tabs/Vdc//Datastores'
|
||||||
|
import Groups from 'client/components/Tabs/Vdc//Groups'
|
||||||
|
import Hosts from 'client/components/Tabs/Vdc//Hosts'
|
||||||
|
import Info from 'client/components/Tabs/Vdc//Info'
|
||||||
|
import Vnets from 'client/components/Tabs/Vdc//Vnets'
|
||||||
|
import Clusters from 'client/components/Tabs/Vdc/Clusters'
|
||||||
|
|
||||||
|
const getTabComponent = (tabName) =>
|
||||||
|
({
|
||||||
|
info: Info,
|
||||||
|
groups: Groups,
|
||||||
|
clusters: Clusters,
|
||||||
|
hosts: Hosts,
|
||||||
|
vnets: Vnets,
|
||||||
|
datastores: Datastores,
|
||||||
|
}[tabName])
|
||||||
|
|
||||||
|
const VDCTabs = memo(({ id }) => {
|
||||||
|
const { view, getResourceView } = useViews()
|
||||||
|
const { isError, error, status, data } = useGetVDCQuery({ id })
|
||||||
|
|
||||||
|
const tabsAvailable = useMemo(() => {
|
||||||
|
const resource = RESOURCE_NAMES.VDC
|
||||||
|
const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {}
|
||||||
|
|
||||||
|
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
|
||||||
|
}, [view, id])
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Alert severity="error" variant="outlined">
|
||||||
|
{error.data}
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'fulfilled' || id === data?.ID) {
|
||||||
|
return <Tabs addBorder tabs={tabsAvailable ?? []} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <LinearProgress color="secondary" sx={{ width: '100%' }} />
|
||||||
|
})
|
||||||
|
VDCTabs.propTypes = { id: PropTypes.string.isRequired }
|
||||||
|
VDCTabs.displayName = 'VDCTabs'
|
||||||
|
|
||||||
|
export default VDCTabs
|
@ -33,7 +33,6 @@ const VmBackupTab = ({ id }) => {
|
|||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
|
||||||
const handleRowClick = (rowId) => {
|
const handleRowClick = (rowId) => {
|
||||||
console.log('going to: ', generatePath(path, { id: String(rowId) }))
|
|
||||||
history.push(generatePath(path, { id: String(rowId) }))
|
history.push(generatePath(path, { id: String(rowId) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +171,7 @@ export const RESOURCE_NAMES = {
|
|||||||
MARKETPLACE: 'marketplace',
|
MARKETPLACE: 'marketplace',
|
||||||
SEC_GROUP: 'security-group',
|
SEC_GROUP: 'security-group',
|
||||||
USER: 'user',
|
USER: 'user',
|
||||||
|
VDC: 'virtual-data-center',
|
||||||
VROUTER: 'virtual-router',
|
VROUTER: 'virtual-router',
|
||||||
VM_TEMPLATE: 'vm-template',
|
VM_TEMPLATE: 'vm-template',
|
||||||
VM: 'vm',
|
VM: 'vm',
|
||||||
@ -202,6 +203,7 @@ export * from 'client/constants/scheduler'
|
|||||||
export * from 'client/constants/securityGroup'
|
export * from 'client/constants/securityGroup'
|
||||||
export * from 'client/constants/user'
|
export * from 'client/constants/user'
|
||||||
export * from 'client/constants/userInput'
|
export * from 'client/constants/userInput'
|
||||||
|
export * from 'client/constants/vdc'
|
||||||
export * from 'client/constants/vm'
|
export * from 'client/constants/vm'
|
||||||
export * from 'client/constants/vmTemplate'
|
export * from 'client/constants/vmTemplate'
|
||||||
export * from 'client/constants/zone'
|
export * from 'client/constants/zone'
|
||||||
|
@ -32,7 +32,8 @@ module.exports = {
|
|||||||
NoLabels: 'NoLabels',
|
NoLabels: 'NoLabels',
|
||||||
All: 'All',
|
All: 'All',
|
||||||
On: 'On',
|
On: 'On',
|
||||||
ToggleAllCurrentPageRowsSelected: 'Toggle all current page rows selected',
|
ToggleAllSelectedCardsCurrentPage:
|
||||||
|
'Toggle all selected cards in current page',
|
||||||
NumberOfResourcesSelected: 'All %s resources are selected',
|
NumberOfResourcesSelected: 'All %s resources are selected',
|
||||||
SelectAllResources: 'Select all %s resources',
|
SelectAllResources: 'Select all %s resources',
|
||||||
ClearSelection: 'Clear selection',
|
ClearSelection: 'Clear selection',
|
||||||
@ -81,6 +82,8 @@ module.exports = {
|
|||||||
CreateServiceTemplate: 'Create Service Template',
|
CreateServiceTemplate: 'Create Service Template',
|
||||||
CreateVirtualNetwork: 'Create Virtual Network',
|
CreateVirtualNetwork: 'Create Virtual Network',
|
||||||
CreateVmTemplate: 'Create VM Template',
|
CreateVmTemplate: 'Create VM Template',
|
||||||
|
CreateVDC: 'Create VDC',
|
||||||
|
UpdateVDC: 'Update VDC',
|
||||||
CurrentGroup: 'Current group: %s',
|
CurrentGroup: 'Current group: %s',
|
||||||
CurrentOwner: 'Current owner: %s',
|
CurrentOwner: 'Current owner: %s',
|
||||||
Delete: 'Delete',
|
Delete: 'Delete',
|
||||||
@ -154,13 +157,17 @@ module.exports = {
|
|||||||
Search: 'Search',
|
Search: 'Search',
|
||||||
Select: 'Select',
|
Select: 'Select',
|
||||||
SelectCluster: 'Select Cluster',
|
SelectCluster: 'Select Cluster',
|
||||||
|
SelectClusters: 'Select Clusters',
|
||||||
SelectDatastore: 'Select a Datastore to store the resource',
|
SelectDatastore: 'Select a Datastore to store the resource',
|
||||||
SelectDatastoreImage: 'Select a Datastore',
|
SelectDatastoreImage: 'Select a Datastore',
|
||||||
|
SelectDatastores: 'Select Datastores',
|
||||||
SelectDockerHubTag: 'Select DockerHub image tag (default latest)',
|
SelectDockerHubTag: 'Select DockerHub image tag (default latest)',
|
||||||
SelectGroup: 'Select a group',
|
SelectGroup: 'Select a group',
|
||||||
SelectHost: 'Select a host',
|
SelectHost: 'Select a host',
|
||||||
|
SelectHosts: 'Select hosts',
|
||||||
SelectMarketplace: 'Select Marketplace',
|
SelectMarketplace: 'Select Marketplace',
|
||||||
SelectNetwork: 'Select a network',
|
SelectNetwork: 'Select a network',
|
||||||
|
SelectVirtualNetworks: 'Select virtual networks',
|
||||||
SelectNewCluster: 'Select a new Cluster',
|
SelectNewCluster: 'Select a new Cluster',
|
||||||
SelectRequest: 'Select request',
|
SelectRequest: 'Select request',
|
||||||
SelectTheNewDatastore: 'Select the new datastore',
|
SelectTheNewDatastore: 'Select the new datastore',
|
||||||
@ -382,6 +389,10 @@ module.exports = {
|
|||||||
NoNetworksInMonitoring:
|
NoNetworksInMonitoring:
|
||||||
'There is currently no network monitoring information associated with this VM',
|
'There is currently no network monitoring information associated with this VM',
|
||||||
|
|
||||||
|
/* sections - vdc */
|
||||||
|
Resources: 'Resources',
|
||||||
|
SelectAllResourcesFromZone: 'Select all %s resources from %s zone (Zone #%s)',
|
||||||
|
|
||||||
/* sections - storage */
|
/* sections - storage */
|
||||||
Backups: 'Backups',
|
Backups: 'Backups',
|
||||||
BackupDatastore: 'Backup Datastore',
|
BackupDatastore: 'Backup Datastore',
|
||||||
@ -531,6 +542,13 @@ module.exports = {
|
|||||||
StandaloneQcow2CloneConcept:
|
StandaloneQcow2CloneConcept:
|
||||||
'Clone qcow2 without a backing chain and no dependencies with Image datastore files',
|
'Clone qcow2 without a backing chain and no dependencies with Image datastore files',
|
||||||
|
|
||||||
|
/* VDC */
|
||||||
|
AllClustersAreIncludedInThisVDC: 'All clusters are included in this VDC',
|
||||||
|
AllHostsAreIncludedInThisVDC: 'All hosts are included in this VDC',
|
||||||
|
AllDatastoresAreIncludedInThisVDC: 'All datastores are included in this VDC',
|
||||||
|
AllVNetworksAreIncludedInThisVDC:
|
||||||
|
'All virtual networks are included in this VDC',
|
||||||
|
|
||||||
/* sections - templates & instances */
|
/* sections - templates & instances */
|
||||||
Instances: 'Instances',
|
Instances: 'Instances',
|
||||||
VM: 'VM',
|
VM: 'VM',
|
||||||
|
58
src/fireedge/src/client/constants/vdc.js
Normal file
58
src/fireedge/src/client/constants/vdc.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 * as ACTIONS from 'client/constants/actions'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VDC
|
||||||
|
* @property {string|number} ID - Id
|
||||||
|
* @property {string} NAME - Name
|
||||||
|
* @property {object} TEMPLATE - Template information
|
||||||
|
* @property {string} [TEMPLATE.DESCRIPTION] - VDC Description
|
||||||
|
* @property {string} [TEMPLATE.LABELS] - VDC Labels
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VDCHost
|
||||||
|
* @property {string} ZONE_ID - Host zone id
|
||||||
|
* @property {string} HOST_ID - Host id
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VDCCluster
|
||||||
|
* @property {string} ZONE_ID - Cluster zone id
|
||||||
|
* @property {string} CLUSTER_ID - Cluster id
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VDCDatastore
|
||||||
|
* @property {string} ZONE_ID - Datastore zone id
|
||||||
|
* @property {string} DATASTORE_ID - Datastore id
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef VDCVnet
|
||||||
|
* @property {string} ZONE_ID - Vnet zone id
|
||||||
|
* @property {string} VNET_ID - Vnet id
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const VDC_ACTIONS = {
|
||||||
|
CREATE_DIALOG: 'create_dialog',
|
||||||
|
UPDATE_DIALOG: 'update_dialog',
|
||||||
|
DELETE: 'delete',
|
||||||
|
RENAME: ACTIONS.RENAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ALL_SELECTED = '-10'
|
36
src/fireedge/src/client/containers/Datastores/Detail.js
Normal file
36
src/fireedge/src/client/containers/Datastores/Detail.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import { useParams, Redirect } from 'react-router-dom'
|
||||||
|
|
||||||
|
import DatastoreTabs from 'client/components/Tabs/Datastore'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the detail information about a Datastore.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement} Datastore detail component.
|
||||||
|
*/
|
||||||
|
function DatatoreDetail() {
|
||||||
|
const { id } = useParams()
|
||||||
|
|
||||||
|
if (Number.isNaN(+id)) {
|
||||||
|
return <Redirect to="/" />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <DatastoreTabs id={id} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatatoreDetail
|
81
src/fireedge/src/client/containers/VDCs/Create.js
Normal file
81
src/fireedge/src/client/containers/VDCs/Create.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import { useHistory, useLocation } from 'react-router'
|
||||||
|
|
||||||
|
import { useGeneralApi } from 'client/features/General'
|
||||||
|
import {
|
||||||
|
useCreateVDCMutation,
|
||||||
|
useUpdateVDCMutation,
|
||||||
|
} from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||||
|
import {
|
||||||
|
DefaultFormStepper,
|
||||||
|
SkeletonStepsForm,
|
||||||
|
} from 'client/components/FormStepper'
|
||||||
|
import { CreateForm } from 'client/components/Forms/Vdc'
|
||||||
|
import { useGetZonesQuery } from 'client/features/OneApi/zone'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the creation or modification form to a VDC Template.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement} VDC Template form
|
||||||
|
*/
|
||||||
|
function CreateVDC() {
|
||||||
|
const history = useHistory()
|
||||||
|
const { state } = useLocation()
|
||||||
|
const { ID: vdcId, NAME } = state ?? {}
|
||||||
|
|
||||||
|
const { data: zones = [] } = useGetZonesQuery()
|
||||||
|
|
||||||
|
const { enqueueSuccess } = useGeneralApi()
|
||||||
|
const [create] = useCreateVDCMutation()
|
||||||
|
const [update] = useUpdateVDCMutation()
|
||||||
|
|
||||||
|
const onSubmit = async (vdc) => {
|
||||||
|
try {
|
||||||
|
if (!vdcId) {
|
||||||
|
const newVDCId = await create(vdc).unwrap()
|
||||||
|
if (newVDCId) {
|
||||||
|
history.push(PATH.SYSTEM.VDCS.LIST)
|
||||||
|
enqueueSuccess(`VDC created - #${newVDCId}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const updatedVDC = await update({ id: vdcId, ...vdc }).unwrap()
|
||||||
|
if (updatedVDC) {
|
||||||
|
history.push(PATH.SYSTEM.VDCS.LIST)
|
||||||
|
enqueueSuccess(`VDC updated - #${vdcId} ${NAME}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return zones.length ? (
|
||||||
|
<CreateForm
|
||||||
|
initialValues={state}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
fallback={<SkeletonStepsForm />}
|
||||||
|
stepProps={zones}
|
||||||
|
>
|
||||||
|
{(config) => <DefaultFormStepper {...config} />}
|
||||||
|
</CreateForm>
|
||||||
|
) : (
|
||||||
|
<SkeletonStepsForm />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateVDC
|
36
src/fireedge/src/client/containers/VDCs/Detail.js
Normal file
36
src/fireedge/src/client/containers/VDCs/Detail.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { ReactElement } from 'react'
|
||||||
|
import { useParams, Redirect } from 'react-router-dom'
|
||||||
|
|
||||||
|
import VDCTabs from 'client/components/Tabs/Vdc'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the detail information about a VDC.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement} VDC detail component.
|
||||||
|
*/
|
||||||
|
function VDCDetail() {
|
||||||
|
const { id } = useParams()
|
||||||
|
|
||||||
|
if (Number.isNaN(+id)) {
|
||||||
|
return <Redirect to="/" />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <VDCTabs id={id} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VDCDetail
|
161
src/fireedge/src/client/containers/VDCs/index.js
Normal file
161
src/fireedge/src/client/containers/VDCs/index.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 { Box, Chip, Stack, Typography } from '@mui/material'
|
||||||
|
import { Cancel, Pin as GotoIcon, RefreshDouble } from 'iconoir-react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { ReactElement, memo, useState } from 'react'
|
||||||
|
import { Row } from 'react-table'
|
||||||
|
|
||||||
|
import { SubmitButton } from 'client/components/FormControl'
|
||||||
|
import { Tr } from 'client/components/HOC'
|
||||||
|
import MultipleTags from 'client/components/MultipleTags'
|
||||||
|
import SplitPane from 'client/components/SplitPane'
|
||||||
|
import { VDCsTable } from 'client/components/Tables'
|
||||||
|
import VDCActions from 'client/components/Tables/VirtualDataCenters/actions'
|
||||||
|
import VDCTabs from 'client/components/Tabs/Vdc'
|
||||||
|
import { T, VmTemplate as VdcTemplate } from 'client/constants'
|
||||||
|
import {
|
||||||
|
useLazyGetVDCQuery,
|
||||||
|
useUpdateVDCMutation,
|
||||||
|
} from 'client/features/OneApi/vdc'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a list of VDCs with a split pane between the list and selected row(s).
|
||||||
|
*
|
||||||
|
* @returns {ReactElement} VDCs list and selected row(s)
|
||||||
|
*/
|
||||||
|
function VirtualDataCenters() {
|
||||||
|
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||||
|
const actions = VDCActions()
|
||||||
|
|
||||||
|
const hasSelectedRows = selectedRows?.length > 0
|
||||||
|
const moreThanOneSelected = selectedRows?.length > 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SplitPane gridTemplateRows="1fr auto 1fr">
|
||||||
|
{({ getGridProps, GutterComponent }) => (
|
||||||
|
<Box height={1} {...(hasSelectedRows && getGridProps())}>
|
||||||
|
<VDCsTable
|
||||||
|
onSelectedRowsChange={onSelectedRowsChange}
|
||||||
|
globalActions={actions}
|
||||||
|
useUpdateMutation={useUpdateVDCMutation}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{hasSelectedRows && (
|
||||||
|
<>
|
||||||
|
<GutterComponent direction="row" track={1} />
|
||||||
|
{moreThanOneSelected ? (
|
||||||
|
<GroupedTags tags={selectedRows} />
|
||||||
|
) : (
|
||||||
|
<InfoTabs
|
||||||
|
template={selectedRows[0]?.original}
|
||||||
|
gotoPage={selectedRows[0]?.gotoPage}
|
||||||
|
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</SplitPane>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays details of a VDC Template.
|
||||||
|
*
|
||||||
|
* @param {VdcTemplate} template - VDC Template id to display
|
||||||
|
* @param {Function} [gotoPage] - Function to navigate to a page of a VDC Template
|
||||||
|
* @param {Function} [unselect] - Function to unselect a VDC Template
|
||||||
|
* @returns {ReactElement} VDC Template details
|
||||||
|
*/
|
||||||
|
const InfoTabs = memo(({ template, gotoPage, unselect }) => {
|
||||||
|
const [getVDC, { data, isFetching }] = useLazyGetVDCQuery()
|
||||||
|
const id = data?.ID ?? template.ID
|
||||||
|
const name = data?.NAME ?? template.NAME
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack overflow="auto">
|
||||||
|
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
|
||||||
|
<Typography color="text.primary" noWrap flexGrow={1}>
|
||||||
|
{`#${id} | ${name}`}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* -- ACTIONS -- */}
|
||||||
|
<SubmitButton
|
||||||
|
data-cy="detail-refresh"
|
||||||
|
icon={<RefreshDouble />}
|
||||||
|
tooltip={Tr(T.Refresh)}
|
||||||
|
isSubmitting={isFetching}
|
||||||
|
onClick={() => getVDC({ id })}
|
||||||
|
/>
|
||||||
|
{typeof gotoPage === 'function' && (
|
||||||
|
<SubmitButton
|
||||||
|
data-cy="locate-on-table"
|
||||||
|
icon={<GotoIcon />}
|
||||||
|
tooltip={Tr(T.LocateOnTable)}
|
||||||
|
onClick={() => gotoPage()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{typeof unselect === 'function' && (
|
||||||
|
<SubmitButton
|
||||||
|
data-cy="unselect"
|
||||||
|
icon={<Cancel />}
|
||||||
|
tooltip={Tr(T.Close)}
|
||||||
|
onClick={() => unselect()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* -- END ACTIONS -- */}
|
||||||
|
</Stack>
|
||||||
|
<VDCTabs id={id} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
InfoTabs.propTypes = {
|
||||||
|
template: PropTypes.object,
|
||||||
|
gotoPage: PropTypes.func,
|
||||||
|
unselect: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoTabs.displayName = 'InfoTabs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a list of tags that represent the selected rows.
|
||||||
|
*
|
||||||
|
* @param {Row[]} tags - Row(s) to display as tags
|
||||||
|
* @returns {ReactElement} List of tags
|
||||||
|
*/
|
||||||
|
const GroupedTags = memo(({ tags = [] }) => (
|
||||||
|
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
|
||||||
|
<MultipleTags
|
||||||
|
limitTags={10}
|
||||||
|
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
|
||||||
|
<Chip
|
||||||
|
key={id}
|
||||||
|
label={original?.NAME ?? id}
|
||||||
|
onClick={gotoPage}
|
||||||
|
onDelete={() => toggleRowSelected(false)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
))
|
||||||
|
|
||||||
|
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||||
|
GroupedTags.displayName = 'GroupedTags'
|
||||||
|
|
||||||
|
export default VirtualDataCenters
|
@ -35,14 +35,16 @@ const clusterApi = oneApi.injectEndpoints({
|
|||||||
/**
|
/**
|
||||||
* Retrieves information for all the clusters in the pool.
|
* Retrieves information for all the clusters in the pool.
|
||||||
*
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {string} [params.zone] - Zone from where to get the resources
|
||||||
* @returns {Cluster[]} List of clusters
|
* @returns {Cluster[]} List of clusters
|
||||||
* @throws Fails when response isn't code 200
|
* @throws Fails when response isn't code 200
|
||||||
*/
|
*/
|
||||||
query: () => {
|
query: (params) => {
|
||||||
const name = Actions.CLUSTER_POOL_INFO
|
const name = Actions.CLUSTER_POOL_INFO
|
||||||
const command = { name, ...Commands[name] }
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
return { command }
|
return { command, params }
|
||||||
},
|
},
|
||||||
transformResponse: (data) => [data?.CLUSTER_POOL?.CLUSTER ?? []].flat(),
|
transformResponse: (data) => [data?.CLUSTER_POOL?.CLUSTER ?? []].flat(),
|
||||||
providesTags: (clusters) =>
|
providesTags: (clusters) =>
|
||||||
|
@ -38,14 +38,16 @@ const datastoreApi = oneApi.injectEndpoints({
|
|||||||
/**
|
/**
|
||||||
* Retrieves information for all or part of the datastores in the pool.
|
* Retrieves information for all or part of the datastores in the pool.
|
||||||
*
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {string} [params.zone] - Zone from where to get the resources
|
||||||
* @returns {Datastore[]} List of datastores
|
* @returns {Datastore[]} List of datastores
|
||||||
* @throws Fails when response isn't code 200
|
* @throws Fails when response isn't code 200
|
||||||
*/
|
*/
|
||||||
query: () => {
|
query: (params) => {
|
||||||
const name = Actions.DATASTORE_POOL_INFO
|
const name = Actions.DATASTORE_POOL_INFO
|
||||||
const command = { name, ...Commands[name] }
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
return { command }
|
return { command, params }
|
||||||
},
|
},
|
||||||
transformResponse: (data) =>
|
transformResponse: (data) =>
|
||||||
[data?.DATASTORE_POOL?.DATASTORE ?? []].flat(),
|
[data?.DATASTORE_POOL?.DATASTORE ?? []].flat(),
|
||||||
|
@ -38,14 +38,16 @@ const hostApi = oneApi.injectEndpoints({
|
|||||||
/**
|
/**
|
||||||
* Retrieves information for all the hosts in the pool.
|
* Retrieves information for all the hosts in the pool.
|
||||||
*
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {string} [params.zone] - Zone from where to get the resources
|
||||||
* @returns {Host[]} Get list of hosts
|
* @returns {Host[]} Get list of hosts
|
||||||
* @throws Fails when response isn't code 200
|
* @throws Fails when response isn't code 200
|
||||||
*/
|
*/
|
||||||
query: () => {
|
query: (params) => {
|
||||||
const name = Actions.HOST_POOL_INFO
|
const name = Actions.HOST_POOL_INFO
|
||||||
const command = { name, ...Commands[name] }
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
return { command }
|
return { command, params }
|
||||||
},
|
},
|
||||||
transformResponse: (data) => [data?.HOST_POOL?.HOST ?? []].flat(),
|
transformResponse: (data) => [data?.HOST_POOL?.HOST ?? []].flat(),
|
||||||
providesTags: (hosts) =>
|
providesTags: (hosts) =>
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
import { createApi } from '@reduxjs/toolkit/query/react'
|
import { createApi } from '@reduxjs/toolkit/query/react'
|
||||||
|
|
||||||
import { enqueueSnackbar } from 'client/features/General/actions'
|
import { enqueueSnackbar } from 'client/features/General/actions'
|
||||||
import { httpCodes } from 'server/utils/constants'
|
import { generateKey, requestConfig } from 'client/utils'
|
||||||
import { requestConfig, generateKey } from 'client/utils'
|
|
||||||
import http from 'client/utils/rest'
|
import http from 'client/utils/rest'
|
||||||
|
import { httpCodes } from 'server/utils/constants'
|
||||||
|
|
||||||
const ONE_RESOURCES = {
|
const ONE_RESOURCES = {
|
||||||
ACL: 'ACL',
|
ACL: 'ACL',
|
||||||
@ -82,13 +82,15 @@ const oneApi = createApi({
|
|||||||
{ params = {}, command, needStateInMeta = false },
|
{ params = {}, command, needStateInMeta = false },
|
||||||
{ getState, dispatch, signal }
|
{ getState, dispatch, signal }
|
||||||
) => {
|
) => {
|
||||||
|
const paramsExtensible = { ...params }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// set filter flag if filter is present in command params
|
// set filter flag if filter is present in command params
|
||||||
if (command?.params?.filter) {
|
if (command?.params?.filter) {
|
||||||
params.filter = getState().auth?.filterPool
|
paramsExtensible.filter = getState().auth?.filterPool
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = requestConfig(params, command)
|
const config = requestConfig(paramsExtensible, command)
|
||||||
const response = await http.request({ ...config, signal })
|
const response = await http.request({ ...config, signal })
|
||||||
const state = needStateInMeta ? getState() : {}
|
const state = needStateInMeta ? getState() : {}
|
||||||
|
|
||||||
@ -124,11 +126,11 @@ const oneApi = createApi({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export {
|
export {
|
||||||
oneApi,
|
|
||||||
ONE_RESOURCES,
|
|
||||||
ONE_RESOURCES_POOL,
|
|
||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
DOCUMENT_POOL,
|
DOCUMENT_POOL,
|
||||||
|
ONE_RESOURCES,
|
||||||
|
ONE_RESOURCES_POOL,
|
||||||
PROVISION_CONFIG,
|
PROVISION_CONFIG,
|
||||||
PROVISION_RESOURCES,
|
PROVISION_RESOURCES,
|
||||||
|
oneApi,
|
||||||
}
|
}
|
||||||
|
@ -16,27 +16,27 @@
|
|||||||
import { Actions, Commands } from 'server/utils/constants/commands/vn'
|
import { Actions, Commands } from 'server/utils/constants/commands/vn'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
oneApi,
|
|
||||||
ONE_RESOURCES,
|
|
||||||
ONE_RESOURCES_POOL,
|
|
||||||
} from 'client/features/OneApi'
|
|
||||||
import {
|
|
||||||
updateResourceOnPool,
|
|
||||||
removeResourceOnPool,
|
|
||||||
updateNameOnResource,
|
|
||||||
updateLockLevelOnResource,
|
|
||||||
removeLockLevelOnResource,
|
|
||||||
updatePermissionOnResource,
|
|
||||||
updateOwnershipOnResource,
|
|
||||||
updateTemplateOnResource,
|
|
||||||
} from 'client/features/OneApi/common'
|
|
||||||
import { UpdateFromSocket } from 'client/features/OneApi/socket'
|
|
||||||
import {
|
|
||||||
LockLevel,
|
|
||||||
FilterFlag,
|
FilterFlag,
|
||||||
|
LockLevel,
|
||||||
Permission,
|
Permission,
|
||||||
VirtualNetwork,
|
VirtualNetwork,
|
||||||
} from 'client/constants'
|
} from 'client/constants'
|
||||||
|
import {
|
||||||
|
ONE_RESOURCES,
|
||||||
|
ONE_RESOURCES_POOL,
|
||||||
|
oneApi,
|
||||||
|
} from 'client/features/OneApi'
|
||||||
|
import {
|
||||||
|
removeLockLevelOnResource,
|
||||||
|
removeResourceOnPool,
|
||||||
|
updateLockLevelOnResource,
|
||||||
|
updateNameOnResource,
|
||||||
|
updateOwnershipOnResource,
|
||||||
|
updatePermissionOnResource,
|
||||||
|
updateResourceOnPool,
|
||||||
|
updateTemplateOnResource,
|
||||||
|
} from 'client/features/OneApi/common'
|
||||||
|
import { UpdateFromSocket } from 'client/features/OneApi/socket'
|
||||||
|
|
||||||
const { VNET } = ONE_RESOURCES
|
const { VNET } = ONE_RESOURCES
|
||||||
const { VNET_POOL } = ONE_RESOURCES_POOL
|
const { VNET_POOL } = ONE_RESOURCES_POOL
|
||||||
@ -51,6 +51,7 @@ const vNetworkApi = oneApi.injectEndpoints({
|
|||||||
* @param {FilterFlag} [params.filter] - Filter flag
|
* @param {FilterFlag} [params.filter] - Filter flag
|
||||||
* @param {number} [params.start] - Range start ID
|
* @param {number} [params.start] - Range start ID
|
||||||
* @param {number} [params.end] - Range end ID
|
* @param {number} [params.end] - Range end ID
|
||||||
|
* @param {string} [params.zone] - Zone from where to get the resources
|
||||||
* @returns {VirtualNetwork[]} List of virtual networks
|
* @returns {VirtualNetwork[]} List of virtual networks
|
||||||
* @throws Fails when response isn't code 200
|
* @throws Fails when response isn't code 200
|
||||||
*/
|
*/
|
||||||
@ -58,7 +59,7 @@ const vNetworkApi = oneApi.injectEndpoints({
|
|||||||
const name = Actions.VN_POOL_INFO
|
const name = Actions.VN_POOL_INFO
|
||||||
const command = { name, ...Commands[name] }
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
return { params, command }
|
return { command, params }
|
||||||
},
|
},
|
||||||
transformResponse: (data) => [data?.VNET_POOL?.VNET ?? []].flat(),
|
transformResponse: (data) => [data?.VNET_POOL?.VNET ?? []].flat(),
|
||||||
providesTags: (networks) =>
|
providesTags: (networks) =>
|
||||||
|
455
src/fireedge/src/client/features/OneApi/vdc.js
Normal file
455
src/fireedge/src/client/features/OneApi/vdc.js
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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 {
|
||||||
|
Actions as CustomActions,
|
||||||
|
Commands as CustomCommands,
|
||||||
|
} from 'server/routes/api/vdc/routes'
|
||||||
|
import { Actions, Commands } from 'server/utils/constants/commands/vdc'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ONE_RESOURCES,
|
||||||
|
ONE_RESOURCES_POOL,
|
||||||
|
oneApi,
|
||||||
|
} from 'client/features/OneApi'
|
||||||
|
import {
|
||||||
|
removeResourceOnPool,
|
||||||
|
updateNameOnResource,
|
||||||
|
updateResourceOnPool,
|
||||||
|
updateTemplateOnResource,
|
||||||
|
} from 'client/features/OneApi/common'
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import {
|
||||||
|
VDCCluster,
|
||||||
|
VDCDatastore,
|
||||||
|
VDCHost,
|
||||||
|
VDCVnet,
|
||||||
|
} from 'client/constants/vdc'
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
const { VDC } = ONE_RESOURCES
|
||||||
|
const { VDC_POOL } = ONE_RESOURCES_POOL
|
||||||
|
|
||||||
|
const vdcApi = oneApi.injectEndpoints({
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
getVDCs: builder.query({
|
||||||
|
/**
|
||||||
|
* Retrieves information for all or part of the Resources in the pool.
|
||||||
|
*
|
||||||
|
* @returns {Array[Object]} List of Virtual Data Centers
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: () => {
|
||||||
|
const name = Actions.VDC_POOL_INFO
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { command }
|
||||||
|
},
|
||||||
|
transformResponse: (data) => [data?.VDC_POOL?.VDC ?? []].flat(),
|
||||||
|
providesTags: (vdcs) =>
|
||||||
|
vdcs
|
||||||
|
? [
|
||||||
|
...vdcs.map(({ ID }) => ({
|
||||||
|
type: VDC_POOL,
|
||||||
|
id: `${ID}`,
|
||||||
|
})),
|
||||||
|
VDC_POOL,
|
||||||
|
]
|
||||||
|
: [VDC_POOL],
|
||||||
|
}),
|
||||||
|
getVDC: builder.query({
|
||||||
|
/**
|
||||||
|
* Retrieves information for the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string} params.id - VDC id
|
||||||
|
* @param {boolean} [params.decrypt] - True to decrypt contained secrets (only admin)
|
||||||
|
* @returns {object} Get VDC identified by id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_INFO
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
transformResponse: (data) => data?.VDC ?? {},
|
||||||
|
providesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
async onQueryStarted({ id }, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const { data: resourceFromQuery } = await queryFulfilled
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDCs',
|
||||||
|
undefined,
|
||||||
|
updateResourceOnPool({ id, resourceFromQuery })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// if the query fails, we want to remove the resource from the pool
|
||||||
|
dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDCs',
|
||||||
|
undefined,
|
||||||
|
removeResourceOnPool({ id })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
createVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Creates a new VDC in OpenNebula.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {string} params.template - A string containing the template on syntax XML
|
||||||
|
* @param {Array[string]} params.groups - List of groups ids
|
||||||
|
* @param {Array[VDCHost]} params.hosts - List of hosts
|
||||||
|
* @param {Array[VDCDatastore]} params.datastores - List of datastores
|
||||||
|
* @param {Array[VDCVnet]} params.vnets - List of vnets
|
||||||
|
* @param {Array[VDCCluster]} params.clusters - List of clusters
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = CustomActions.VDC_CREATE
|
||||||
|
const command = { name, ...CustomCommands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
removeVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Deletes the given VDC from the pool.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {number|string} params.id - VDC id
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELETE
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: [VDC_POOL],
|
||||||
|
}),
|
||||||
|
updateVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Replaces the template contents.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request params
|
||||||
|
* @param {number|string} params.id - VDC id
|
||||||
|
* @param {string} params.template - The new template contents
|
||||||
|
* @param {0|1} params.replace
|
||||||
|
* - Update type:
|
||||||
|
* ``0``: Replace the whole template.
|
||||||
|
* ``1``: Merge new template with the existing one.
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = CustomActions.VDC_UPDATE
|
||||||
|
const command = { name, ...CustomCommands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const patchVDC = dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDC',
|
||||||
|
{ id: params.id },
|
||||||
|
updateTemplateOnResource(params)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const patchVDCs = dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDCs',
|
||||||
|
undefined,
|
||||||
|
updateTemplateOnResource(params)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
queryFulfilled.catch(() => {
|
||||||
|
patchVDC.undo()
|
||||||
|
patchVDCs.undo()
|
||||||
|
})
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
renameVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Renames a Virtual Data Center.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {string} params.name - The new name
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_RENAME
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||||
|
try {
|
||||||
|
const patchVDC = dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDC',
|
||||||
|
{ id: params.id },
|
||||||
|
updateNameOnResource(params)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const patchVDCs = dispatch(
|
||||||
|
vdcApi.util.updateQueryData(
|
||||||
|
'getVDCs',
|
||||||
|
undefined,
|
||||||
|
updateNameOnResource(params)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
queryFulfilled.catch(() => {
|
||||||
|
patchVDC.undo()
|
||||||
|
patchVDCs.undo()
|
||||||
|
})
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
addGroupToVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Adds a group to the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {string} params.group - Group id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_ADDGROUP
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
removeGroupFromVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Removes a group from the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {string} params.group - Group id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELGROUP
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
addClusterToVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Adds a Cluster to the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.cluster - Cluster id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_ADDCLUSTER
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
removeClusterFromVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Removes a Cluster from the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.cluster - Cluster id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELCLUSTER
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
addHostToVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Adds a Host to the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.host - Host id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_ADDHOST
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
removeHostFromVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Removes a Host from the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.host - Host id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELHOST
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
addDatastoreToVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Adds a Datastore to the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.datastore - Datastore id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_ADDDATASTORE
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
removeDatastoreFromVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Removes a Datastore from the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.datastore - Datastore id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELDATASTORE
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
addVNetToVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Adds a VNet to the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.vnet - VNet id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_ADDVNET
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
removeVNetFromVDC: builder.mutation({
|
||||||
|
/**
|
||||||
|
* Removes a VNet from the VDC.
|
||||||
|
*
|
||||||
|
* @param {object} params - Request parameters
|
||||||
|
* @param {string|number} params.id - VDC id
|
||||||
|
* @param {number} params.zone - Zone id
|
||||||
|
* @param {string} params.vnet - VNet id to be added to the VDC
|
||||||
|
* @returns {number} VDC id
|
||||||
|
* @throws Fails when response isn't code 200
|
||||||
|
*/
|
||||||
|
query: (params) => {
|
||||||
|
const name = Actions.VDC_DELVNET
|
||||||
|
const command = { name, ...Commands[name] }
|
||||||
|
|
||||||
|
return { params, command }
|
||||||
|
},
|
||||||
|
invalidatesTags: (_, __, { id }) => [{ type: VDC, id }],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const {
|
||||||
|
// Queries
|
||||||
|
useGetVDCsQuery,
|
||||||
|
useLazyGetVDCsQuery,
|
||||||
|
useGetVDCQuery,
|
||||||
|
useLazyGetVDCQuery,
|
||||||
|
|
||||||
|
// Mutations
|
||||||
|
useCreateVDCMutation,
|
||||||
|
useRemoveVDCMutation,
|
||||||
|
useUpdateVDCMutation,
|
||||||
|
useRenameVDCMutation,
|
||||||
|
useAddGroupToVDCMutation,
|
||||||
|
useRemoveGroupFromVDCMutation,
|
||||||
|
useAddClusterToVDCMutation,
|
||||||
|
useRemoveClusterFromVDCMutation,
|
||||||
|
useAddHostToVDCMutation,
|
||||||
|
useRemoveHostFromVDCMutation,
|
||||||
|
useAddDatastoreToVDCMutation,
|
||||||
|
useRemoveDatastoreFromVDCMutation,
|
||||||
|
useAddVNetToVDCMutation,
|
||||||
|
useRemoveVNetFromVDCMutation,
|
||||||
|
} = vdcApi
|
||||||
|
|
||||||
|
export default vdcApi
|
@ -49,6 +49,7 @@ const routes = [
|
|||||||
'sunstone',
|
'sunstone',
|
||||||
'system',
|
'system',
|
||||||
'support',
|
'support',
|
||||||
|
'vdc',
|
||||||
]
|
]
|
||||||
|
|
||||||
const serverRoutes = []
|
const serverRoutes = []
|
||||||
|
701
src/fireedge/src/server/routes/api/vdc/functions.js
Normal file
701
src/fireedge/src/server/routes/api/vdc/functions.js
Normal file
@ -0,0 +1,701 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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. *
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
const { defaults, httpCodes } = require('server/utils/constants')
|
||||||
|
const { httpResponse } = require('server/utils/server')
|
||||||
|
const { Actions: vdcActions } = require('server/utils/constants/commands/vdc')
|
||||||
|
|
||||||
|
const { ok, badRequest } = httpCodes
|
||||||
|
const { defaultEmptyFunction } = defaults
|
||||||
|
|
||||||
|
const {
|
||||||
|
VDC_ALLOCATE,
|
||||||
|
VDC_ADDHOST,
|
||||||
|
VDC_ADDCLUSTER,
|
||||||
|
VDC_ADDGROUP,
|
||||||
|
VDC_ADDDATASTORE,
|
||||||
|
VDC_ADDVNET,
|
||||||
|
VDC_UPDATE,
|
||||||
|
VDC_INFO,
|
||||||
|
VDC_DELGROUP,
|
||||||
|
VDC_DELCLUSTER,
|
||||||
|
VDC_DELDATASTORE,
|
||||||
|
VDC_DELHOST,
|
||||||
|
VDC_DELVNET,
|
||||||
|
} = vdcActions
|
||||||
|
|
||||||
|
const resourcesForDelete = (xmlData, externalData, xmlKey, externalKey) => {
|
||||||
|
const resourceVDC = xmlData
|
||||||
|
? Array.isArray(xmlData)
|
||||||
|
? xmlData
|
||||||
|
: [xmlData]
|
||||||
|
: []
|
||||||
|
|
||||||
|
return resourceVDC.filter((item) => {
|
||||||
|
const { ZONE_ID } = item
|
||||||
|
const found = externalData.find(
|
||||||
|
(resource) =>
|
||||||
|
resource.zone_id === ZONE_ID &&
|
||||||
|
resource[externalKey].includes(item[xmlKey])
|
||||||
|
)
|
||||||
|
|
||||||
|
return !found
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourcesForAdd = (xmlData, externalData, xmlKey, externalKey) => {
|
||||||
|
const resourceVDC = xmlData
|
||||||
|
? Array.isArray(xmlData)
|
||||||
|
? xmlData
|
||||||
|
: [xmlData]
|
||||||
|
: []
|
||||||
|
|
||||||
|
return externalData.map((obj) => {
|
||||||
|
const { zone_id: zoneId } = obj
|
||||||
|
const newItems = obj[externalKey].filter((val) => {
|
||||||
|
const exist = resourceVDC.some(
|
||||||
|
(item) => item.ZONE_ID === zoneId && item[xmlKey] === val
|
||||||
|
)
|
||||||
|
|
||||||
|
return !exist
|
||||||
|
})
|
||||||
|
|
||||||
|
return { zone_id: zoneId, [externalKey]: newItems }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const limitResourceAdd = (data, key) =>
|
||||||
|
data.reduce((suma, obj) => suma + (obj?.[key]?.length || 0), 0)
|
||||||
|
|
||||||
|
const groupsForVDC = (externalData, xmlData, del = true) => {
|
||||||
|
const arrayXmlData = xmlData
|
||||||
|
? Array.isArray(xmlData)
|
||||||
|
? xmlData
|
||||||
|
: [xmlData]
|
||||||
|
: []
|
||||||
|
|
||||||
|
return del
|
||||||
|
? arrayXmlData.filter((value) => !externalData.includes(value))
|
||||||
|
: externalData.filter((value) => !arrayXmlData.includes(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create VDC.
|
||||||
|
*
|
||||||
|
* @param {object} res - http response
|
||||||
|
* @param {Function} next - express stepper
|
||||||
|
* @param {object} params - params of http request
|
||||||
|
* @param {string} params.hosts - vdc hosts
|
||||||
|
* @param {string} params.datastores - vdc datastores
|
||||||
|
* @param {string} params.vnets - vdc vnets
|
||||||
|
* @param {string} params.groups - vdc groups
|
||||||
|
* @param {string} params.clusters - vdc clusterts
|
||||||
|
* @param {string} params.template - vdc template
|
||||||
|
* @param {object} userData - user of http request
|
||||||
|
* @param {Function} xmlrpc - XML-RPC function
|
||||||
|
*/
|
||||||
|
const createVdc = (
|
||||||
|
res = {},
|
||||||
|
next = defaultEmptyFunction,
|
||||||
|
params = {},
|
||||||
|
userData = {},
|
||||||
|
xmlrpc = defaultEmptyFunction
|
||||||
|
) => {
|
||||||
|
const { user, password } = userData
|
||||||
|
const { hosts, datastores, vnets, groups, clusters, template } = params
|
||||||
|
|
||||||
|
const clustersArray = clusters || []
|
||||||
|
const hostsArray = hosts || []
|
||||||
|
const datastoresArray = datastores || []
|
||||||
|
const vnetsArray = vnets || []
|
||||||
|
const groupsArray = groups || []
|
||||||
|
|
||||||
|
const oneClient = xmlrpc(user, password)
|
||||||
|
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ALLOCATE,
|
||||||
|
parameters: [template],
|
||||||
|
callback: (vdcInfoErr, vdcId) => {
|
||||||
|
if (vdcInfoErr || Number.isNaN(Number(vdcId))) {
|
||||||
|
res.locals.httpCode = httpResponse(badRequest, vdcInfoErr)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const vdcErrors = {
|
||||||
|
hosts: [],
|
||||||
|
datastores: [],
|
||||||
|
vnets: [],
|
||||||
|
clusters: [],
|
||||||
|
groups: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send Cluster to VDC
|
||||||
|
// Cluster format should be [ { zone_id: 0, cluster_id: 0 } ]
|
||||||
|
clustersArray.forEach(
|
||||||
|
({ zone_id: zoneId, cluster_id: internalClusters }) => {
|
||||||
|
internalClusters.forEach((clusterId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDCLUSTER,
|
||||||
|
parameters: [
|
||||||
|
parseInt(vdcId, 10),
|
||||||
|
parseInt(zoneId, 10),
|
||||||
|
parseInt(clusterId, 10),
|
||||||
|
],
|
||||||
|
callback: (err, id) => {
|
||||||
|
if (err || !id) {
|
||||||
|
vdcErrors.clusters.push(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Send hosts to VDC
|
||||||
|
// Host format should be [ { zone_id: 0, host_id: 0 } ]
|
||||||
|
hostsArray.forEach(({ zone_id: zoneId, host_id: internalHosts }) => {
|
||||||
|
internalHosts.forEach((hostId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDHOST,
|
||||||
|
parameters: [
|
||||||
|
parseInt(vdcId, 10),
|
||||||
|
parseInt(zoneId, 10),
|
||||||
|
parseInt(hostId, 10),
|
||||||
|
],
|
||||||
|
callback: (err, id) => {
|
||||||
|
if (err || !id) {
|
||||||
|
vdcErrors.hosts.push(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Send datastores to VDC
|
||||||
|
// Datastore format should be [ { zone_id: 0, ds_id: 0 } ]
|
||||||
|
datastoresArray.forEach(
|
||||||
|
({ zone_id: zoneId, ds_id: internalDatastores }) => {
|
||||||
|
internalDatastores.forEach((dsId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDDATASTORE,
|
||||||
|
parameters: [
|
||||||
|
parseInt(vdcId, 10),
|
||||||
|
parseInt(zoneId, 10),
|
||||||
|
parseInt(dsId, 10),
|
||||||
|
],
|
||||||
|
callback: (err, id) => {
|
||||||
|
if (err || !id) {
|
||||||
|
vdcErrors.datastores.push(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Send Virtual Networks to VDC
|
||||||
|
// VNet format should be [ { zone_id: 0, vnet_id: 0 } ]
|
||||||
|
vnetsArray.forEach(({ zone_id: zoneId, vnet_id: internalDatastores }) => {
|
||||||
|
internalDatastores.forEach((vnetId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDVNET,
|
||||||
|
parameters: [
|
||||||
|
parseInt(vdcId, 10),
|
||||||
|
parseInt(zoneId, 10),
|
||||||
|
parseInt(vnetId, 10),
|
||||||
|
],
|
||||||
|
callback: (err, id) => {
|
||||||
|
if (err || !id) {
|
||||||
|
vdcErrors.vnets.push(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Send Groups to VDC
|
||||||
|
groupsArray.forEach((groupId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDGROUP,
|
||||||
|
parameters: [parseInt(vdcId, 10), parseInt(groupId, 10)],
|
||||||
|
callback: (err, id) => {
|
||||||
|
if (err || !id) {
|
||||||
|
vdcErrors.groups.push(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// check if any of the errors is not empty
|
||||||
|
const hasErrors = Object.values(vdcErrors).some(
|
||||||
|
(errorArray) => errorArray.length > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (hasErrors) {
|
||||||
|
let errorMessage = ''
|
||||||
|
Object.entries(vdcErrors).forEach(([resource, errors]) => {
|
||||||
|
if (errors.length > 0) {
|
||||||
|
errorMessage += `${resource}: ${errors.join(', ')}\n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
res.locals.httpCode = httpResponse(ok, errorMessage)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.httpCode = httpResponse(ok, vdcId)
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update VDC.
|
||||||
|
*
|
||||||
|
* @param {object} res - http response
|
||||||
|
* @param {Function} next - express stepper
|
||||||
|
* @param {object} params - params of http request
|
||||||
|
* @param {string} params.id - vdc ID
|
||||||
|
* @param {string} params.hosts - vdc hosts
|
||||||
|
* @param {string} params.datastores - vdc datastores
|
||||||
|
* @param {string} params.vnets - vdc vnets
|
||||||
|
* @param {string} params.groups - vdc groups
|
||||||
|
* @param {string} params.clusters - vdc clusterts
|
||||||
|
* @param {string} params.template - vdc template
|
||||||
|
* @param {object} userData - user of http request
|
||||||
|
* @param {Function} xmlrpc - XML-RPC function
|
||||||
|
*/
|
||||||
|
const updateVdc = (
|
||||||
|
res = {},
|
||||||
|
next = defaultEmptyFunction,
|
||||||
|
params = {},
|
||||||
|
userData = {},
|
||||||
|
xmlrpc = defaultEmptyFunction
|
||||||
|
) => {
|
||||||
|
const { user, password } = userData
|
||||||
|
const { hosts, datastores, vnets, groups, clusters, template, id } = params
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
res.locals.httpCode = httpResponse(badRequest)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const vdcID = parseInt(id, 10)
|
||||||
|
const oneClient = xmlrpc(user, password)
|
||||||
|
|
||||||
|
const clustersArray = clusters || []
|
||||||
|
const datastoresArray = datastores || []
|
||||||
|
const hostsArray = hosts || []
|
||||||
|
const vnetsArray = vnets || []
|
||||||
|
const groupsArray = groups || []
|
||||||
|
|
||||||
|
const vdcErrors = {
|
||||||
|
clusters: { add: [], del: [] },
|
||||||
|
datastores: { add: [], del: [] },
|
||||||
|
hosts: { add: [], del: [] },
|
||||||
|
vnets: { add: [], del: [] },
|
||||||
|
groups: { add: [], del: [] },
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
groups: { add: 0, delete: 0 },
|
||||||
|
clusters: { add: 0, delete: 0 },
|
||||||
|
datastores: { add: 0, delete: 0 },
|
||||||
|
hosts: { add: 0, delete: 0 },
|
||||||
|
vnets: { add: 0, delete: 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackRequest = ({
|
||||||
|
groups: groupsProcessed,
|
||||||
|
clusters: clustersProcessed,
|
||||||
|
datastores: datastoresProcessed,
|
||||||
|
hosts: hostsProcessed,
|
||||||
|
vnets: vnetsProcessed,
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
groupsProcessed.add.limit === groupsProcessed.add.process &&
|
||||||
|
clustersProcessed.add.limit === clustersProcessed.add.process &&
|
||||||
|
datastoresProcessed.add.limit === datastoresProcessed.add.process &&
|
||||||
|
hostsProcessed.add.limit === hostsProcessed.add.process &&
|
||||||
|
vnetsProcessed.add.limit === vnetsProcessed.add.process &&
|
||||||
|
groupsProcessed.delete.limit === groupsProcessed.delete.process &&
|
||||||
|
clustersProcessed.delete.limit === clustersProcessed.delete.process &&
|
||||||
|
datastoresProcessed.delete.limit === datastoresProcessed.delete.process &&
|
||||||
|
hostsProcessed.delete.limit === hostsProcessed.delete.process &&
|
||||||
|
vnetsProcessed.delete.limit === vnetsProcessed.delete.process
|
||||||
|
) {
|
||||||
|
const hasAddErrors = Object.values(vdcErrors).some(
|
||||||
|
(errorArray) => errorArray.add.length > 0
|
||||||
|
)
|
||||||
|
const hasDelErrors = Object.values(vdcErrors).some(
|
||||||
|
(errorArray) => errorArray.del.length > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (hasAddErrors || hasDelErrors) {
|
||||||
|
let errorMessage = ''
|
||||||
|
Object.entries(vdcErrors).forEach(([resource, errors]) => {
|
||||||
|
if (errors.add.length > 0) {
|
||||||
|
errorMessage += `add ${resource}: ${errors.join(', ')}\n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.entries(vdcErrors).forEach(([resource, errors]) => {
|
||||||
|
if (errors.del.length > 0) {
|
||||||
|
errorMessage += `delete ${resource}: ${errors.join(', ')}\n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
res.locals.httpCode = httpResponse(ok, errorMessage)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.locals.httpCode = httpResponse(ok, id)
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackInfo = (vdcInfo) => {
|
||||||
|
const groupsForDelete = groupsForVDC(groupsArray, vdcInfo?.GROUPS?.ID)
|
||||||
|
const groupsForAdd = groupsForVDC(groupsArray, vdcInfo?.GROUPS?.ID, false)
|
||||||
|
const clustersForDelete = resourcesForDelete(
|
||||||
|
vdcInfo?.CLUSTERS?.CLUSTER,
|
||||||
|
clustersArray,
|
||||||
|
'CLUSTER_ID',
|
||||||
|
'cluster_id'
|
||||||
|
)
|
||||||
|
const clustersForAdd = resourcesForAdd(
|
||||||
|
vdcInfo?.CLUSTERS?.CLUSTER,
|
||||||
|
clustersArray,
|
||||||
|
'CLUSTER_ID',
|
||||||
|
'cluster_id'
|
||||||
|
)
|
||||||
|
const datastoresForDelete = resourcesForDelete(
|
||||||
|
vdcInfo?.DATASTORES?.DATASTORE,
|
||||||
|
datastoresArray,
|
||||||
|
'DATASTORE_ID',
|
||||||
|
'ds_id'
|
||||||
|
)
|
||||||
|
const datastoresForAdd = resourcesForAdd(
|
||||||
|
vdcInfo?.DATASTORES?.DATASTORE,
|
||||||
|
datastoresArray,
|
||||||
|
'DATASTORE_ID',
|
||||||
|
'ds_id'
|
||||||
|
)
|
||||||
|
const hostsForDelete = resourcesForDelete(
|
||||||
|
vdcInfo?.HOSTS?.HOST,
|
||||||
|
hostsArray,
|
||||||
|
'HOST_ID',
|
||||||
|
'host_id'
|
||||||
|
)
|
||||||
|
const hostsForAdd = resourcesForAdd(
|
||||||
|
vdcInfo?.HOSTS?.HOST,
|
||||||
|
hostsArray,
|
||||||
|
'HOST_ID',
|
||||||
|
'host_id'
|
||||||
|
)
|
||||||
|
|
||||||
|
const vnetsForDelete = resourcesForDelete(
|
||||||
|
vdcInfo?.VNETS?.VNET,
|
||||||
|
vnetsArray,
|
||||||
|
'VNET_ID',
|
||||||
|
'vnet_id'
|
||||||
|
)
|
||||||
|
|
||||||
|
const vnetsForAdd = resourcesForAdd(
|
||||||
|
vdcInfo?.VNETS?.VNET,
|
||||||
|
vnetsArray,
|
||||||
|
'VNET_ID',
|
||||||
|
'vnet_id'
|
||||||
|
)
|
||||||
|
|
||||||
|
const generateResult = () => {
|
||||||
|
callbackRequest({
|
||||||
|
groups: {
|
||||||
|
add: {
|
||||||
|
limit: groupsForAdd.length,
|
||||||
|
process: results.groups.add,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
limit: groupsForDelete.length,
|
||||||
|
process: results.groups.delete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
clusters: {
|
||||||
|
add: {
|
||||||
|
limit: limitResourceAdd(clustersForAdd, 'cluster_id'),
|
||||||
|
process: results.clusters.add,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
limit: clustersForDelete.length,
|
||||||
|
process: results.clusters.delete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
datastores: {
|
||||||
|
add: {
|
||||||
|
limit: limitResourceAdd(datastoresForAdd, 'ds_id'),
|
||||||
|
process: results.datastores.add,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
limit: datastoresForDelete.length,
|
||||||
|
process: results.datastores.delete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hosts: {
|
||||||
|
add: {
|
||||||
|
limit: limitResourceAdd(hostsForAdd, 'host_id'),
|
||||||
|
process: results.hosts.add,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
limit: hostsForDelete.length,
|
||||||
|
process: results.hosts.delete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vnets: {
|
||||||
|
add: {
|
||||||
|
limit: limitResourceAdd(vnetsForAdd, 'vnet_id'),
|
||||||
|
process: results.vnets.add,
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
limit: vnetsForDelete.length,
|
||||||
|
process: results.vnets.delete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete resources from VDC */
|
||||||
|
// GROUPS
|
||||||
|
groupsForDelete.forEach((idGroup) => {
|
||||||
|
const group = parseInt(idGroup, 10)
|
||||||
|
if (!Number.isNaN(Number(group))) {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_DELGROUP,
|
||||||
|
parameters: [vdcID, group],
|
||||||
|
callback: (err, idDeletedGroup) => {
|
||||||
|
results.groups.delete = results.groups.delete + 1
|
||||||
|
if (err || Number.isNaN(Number(idDeletedGroup))) {
|
||||||
|
vdcErrors.groups.del.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// CLUSTERS
|
||||||
|
clustersForDelete.forEach((resource) => {
|
||||||
|
const zone = parseInt(resource?.ZONE_ID, 10)
|
||||||
|
const cluster = parseInt(resource?.CLUSTER_ID, 10)
|
||||||
|
if (!Number.isNaN(Number(zone)) && !Number.isNaN(Number(cluster))) {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_DELCLUSTER,
|
||||||
|
parameters: [vdcID, zone, cluster],
|
||||||
|
callback: (err, idDeletedClusters) => {
|
||||||
|
results.clusters.delete = results.clusters.delete + 1
|
||||||
|
if (err || Number.isNaN(Number(idDeletedClusters))) {
|
||||||
|
vdcErrors.clusters.del.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// DATASTORES
|
||||||
|
datastoresForDelete.forEach((resource) => {
|
||||||
|
const zone = parseInt(resource?.ZONE_ID, 10)
|
||||||
|
const ds = parseInt(resource?.DATASTORE_ID, 10)
|
||||||
|
if (!Number.isNaN(Number(zone)) && !Number.isNaN(Number(ds))) {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_DELDATASTORE,
|
||||||
|
parameters: [vdcID, zone, ds],
|
||||||
|
callback: (err, idDeletedDS) => {
|
||||||
|
results.datastores.delete = results.datastores.delete + 1
|
||||||
|
if (err || Number.isNaN(Number(idDeletedDS))) {
|
||||||
|
vdcErrors.datastores.del.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// HOSTS
|
||||||
|
hostsForDelete.forEach((resource) => {
|
||||||
|
const zone = parseInt(resource?.ZONE_ID, 10)
|
||||||
|
const host = parseInt(resource?.HOST_ID, 10)
|
||||||
|
if (!Number.isNaN(Number(zone)) && !Number.isNaN(Number(host))) {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_DELHOST,
|
||||||
|
parameters: [vdcID, zone, host],
|
||||||
|
callback: (err, idDeletedHosts) => {
|
||||||
|
results.hosts.delete = results.hosts.delete + 1
|
||||||
|
if (err || Number.isNaN(Number(idDeletedHosts))) {
|
||||||
|
vdcErrors.hosts.del.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// VNETS
|
||||||
|
vnetsForDelete.forEach((resource) => {
|
||||||
|
const zone = parseInt(resource?.ZONE_ID, 10)
|
||||||
|
const vnet = parseInt(resource?.VNET_ID, 10)
|
||||||
|
if (!Number.isNaN(Number(zone)) && !Number.isNaN(Number(vnet))) {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_DELVNET,
|
||||||
|
parameters: [vdcID, zone, vnet],
|
||||||
|
callback: (err, idDeletedVnets) => {
|
||||||
|
results.vnets.delete = results.vnets.delete + 1
|
||||||
|
if (err || Number.isNaN(Number(idDeletedVnets))) {
|
||||||
|
vdcErrors.vnets.del.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Add resources from VDC */
|
||||||
|
// GROUPS
|
||||||
|
groupsForAdd.forEach((groupId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDGROUP,
|
||||||
|
parameters: [vdcID, parseInt(groupId, 10)],
|
||||||
|
callback: (err, idGroup) => {
|
||||||
|
results.groups.add = results.groups.add + 1
|
||||||
|
if (err || Number.isNaN(Number(idGroup))) {
|
||||||
|
vdcErrors.groups.add.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// CLUSTERS
|
||||||
|
clustersForAdd.forEach(
|
||||||
|
({ zone_id: zoneId, cluster_id: internalClusters }) => {
|
||||||
|
internalClusters.forEach((clusterId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDCLUSTER,
|
||||||
|
parameters: [vdcID, parseInt(zoneId, 10), parseInt(clusterId, 10)],
|
||||||
|
callback: (err, idCluster) => {
|
||||||
|
results.clusters.add = results.clusters.add + 1
|
||||||
|
if (err || Number.isNaN(Number(idCluster))) {
|
||||||
|
vdcErrors.clusters.add.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DATASTORES
|
||||||
|
datastoresForAdd.forEach(
|
||||||
|
({ zone_id: zoneId, ds_id: internalDatastores }) => {
|
||||||
|
internalDatastores.forEach((dsId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDDATASTORE,
|
||||||
|
parameters: [vdcID, parseInt(zoneId, 10), parseInt(dsId, 10)],
|
||||||
|
callback: (err, idDs) => {
|
||||||
|
results.datastores.add = results.datastores.add + 1
|
||||||
|
if (err || Number.isNaN(Number(idDs))) {
|
||||||
|
vdcErrors.datastores.add.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// HOSTS
|
||||||
|
hostsForAdd.forEach(({ zone_id: zoneId, host_id: internalHosts }) => {
|
||||||
|
internalHosts.forEach((hostId) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDHOST,
|
||||||
|
parameters: [vdcID, parseInt(zoneId, 10), parseInt(hostId, 10)],
|
||||||
|
callback: (err, idHost) => {
|
||||||
|
results.hosts.add = results.hosts.add + 1
|
||||||
|
if (err || Number.isNaN(Number(idHost))) {
|
||||||
|
vdcErrors.hosts.add.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// VNETS
|
||||||
|
vnetsForAdd.forEach(({ zone_id: zoneId, vnet_id: internalDatastores }) => {
|
||||||
|
internalDatastores.forEach((vnetId, index) => {
|
||||||
|
oneClient({
|
||||||
|
action: VDC_ADDVNET,
|
||||||
|
parameters: [vdcID, parseInt(zoneId, 10), parseInt(vnetId, 10)],
|
||||||
|
callback: (err, idVnet) => {
|
||||||
|
results.vnets.add = results.vnets.add + 1
|
||||||
|
if (err || Number.isNaN(Number(idVnet))) {
|
||||||
|
vdcErrors.vnets.add.push(err)
|
||||||
|
}
|
||||||
|
generateResult()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
generateResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
oneClient({
|
||||||
|
action: VDC_UPDATE,
|
||||||
|
parameters: [vdcID, template],
|
||||||
|
callback: (vdcInfoErr, vdcId) => {
|
||||||
|
if (vdcInfoErr || Number.isNaN(Number(vdcId))) {
|
||||||
|
res.locals.httpCode = httpResponse(badRequest, vdcInfoErr)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oneClient({
|
||||||
|
action: VDC_INFO,
|
||||||
|
parameters: [vdcID],
|
||||||
|
callback: (err, vdcInfo) => {
|
||||||
|
if (err || !vdcInfo?.VDC) {
|
||||||
|
res.locals.httpCode = httpResponse(badRequest, err)
|
||||||
|
next()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackInfo(vdcInfo?.VDC)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createVdc,
|
||||||
|
updateVdc,
|
||||||
|
}
|
31
src/fireedge/src/server/routes/api/vdc/index.js
Normal file
31
src/fireedge/src/server/routes/api/vdc/index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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. *
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
const { Actions, Commands } = require('server/routes/api/vdc/routes')
|
||||||
|
const { createVdc, updateVdc } = require('server/routes/api/vdc/functions')
|
||||||
|
|
||||||
|
const { VDC_CREATE, VDC_UPDATE } = Actions
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
...Commands[VDC_CREATE],
|
||||||
|
action: createVdc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...Commands[VDC_UPDATE],
|
||||||
|
action: updateVdc,
|
||||||
|
},
|
||||||
|
]
|
91
src/fireedge/src/server/routes/api/vdc/routes.js
Normal file
91
src/fireedge/src/server/routes/api/vdc/routes.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/* ------------------------------------------------------------------------- *
|
||||||
|
* Copyright 2002-2023, 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. *
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
const {
|
||||||
|
httpMethod,
|
||||||
|
from: fromData,
|
||||||
|
} = require('../../../utils/constants/defaults')
|
||||||
|
|
||||||
|
const basepath = '/vdc'
|
||||||
|
const { POST, PUT } = httpMethod
|
||||||
|
const { resource, postBody } = fromData
|
||||||
|
|
||||||
|
const VDC_CREATE = 'vdc.create'
|
||||||
|
const VDC_UPDATE = 'vdc.updateVdc'
|
||||||
|
|
||||||
|
const Actions = {
|
||||||
|
VDC_CREATE,
|
||||||
|
VDC_UPDATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Actions,
|
||||||
|
Commands: {
|
||||||
|
[VDC_CREATE]: {
|
||||||
|
path: `${basepath}/create`,
|
||||||
|
httpMethod: POST,
|
||||||
|
auth: true,
|
||||||
|
params: {
|
||||||
|
hosts: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
datastores: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
vnets: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
clusters: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[VDC_UPDATE]: {
|
||||||
|
path: `${basepath}/updateVdc/:id`,
|
||||||
|
httpMethod: PUT,
|
||||||
|
auth: true,
|
||||||
|
params: {
|
||||||
|
id: {
|
||||||
|
from: resource,
|
||||||
|
},
|
||||||
|
hosts: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
datastores: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
vnets: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
clusters: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
from: postBody,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
@ -202,7 +202,12 @@ module.exports = {
|
|||||||
[CLUSTER_POOL_INFO]: {
|
[CLUSTER_POOL_INFO]: {
|
||||||
// inspected
|
// inspected
|
||||||
httpMethod: GET,
|
httpMethod: GET,
|
||||||
params: {},
|
params: {
|
||||||
|
zone: {
|
||||||
|
from: query,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,12 @@ module.exports = {
|
|||||||
[DATASTORE_POOL_INFO]: {
|
[DATASTORE_POOL_INFO]: {
|
||||||
// inspected
|
// inspected
|
||||||
httpMethod: GET,
|
httpMethod: GET,
|
||||||
params: {},
|
params: {
|
||||||
|
zone: {
|
||||||
|
from: query,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,12 @@ module.exports = {
|
|||||||
[HOST_POOL_INFO]: {
|
[HOST_POOL_INFO]: {
|
||||||
// inspected
|
// inspected
|
||||||
httpMethod: GET,
|
httpMethod: GET,
|
||||||
params: {},
|
params: {
|
||||||
|
zone: {
|
||||||
|
from: query,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
[HOST_POOL_MONITORING]: {
|
[HOST_POOL_MONITORING]: {
|
||||||
// inspected
|
// inspected
|
||||||
|
@ -66,10 +66,6 @@ module.exports = {
|
|||||||
from: postBody,
|
from: postBody,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
cluster: {
|
|
||||||
from: postBody,
|
|
||||||
default: -1,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[VDC_DELETE]: {
|
[VDC_DELETE]: {
|
||||||
|
@ -348,6 +348,10 @@ module.exports = {
|
|||||||
from: query,
|
from: query,
|
||||||
default: -1,
|
default: -1,
|
||||||
},
|
},
|
||||||
|
zone: {
|
||||||
|
from: query,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -127,7 +127,7 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => {
|
|||||||
fillHookResource = true,
|
fillHookResource = true,
|
||||||
parseXML = true,
|
parseXML = true,
|
||||||
}) => {
|
}) => {
|
||||||
if (action && parameters && Array.isArray(parameters) && callback) {
|
if (action && Array.isArray(parameters) && callback) {
|
||||||
// user config
|
// user config
|
||||||
const appConfig = getFireedgeConfig()
|
const appConfig = getFireedgeConfig()
|
||||||
const namespace = appConfig.namespace || defaultNamespace
|
const namespace = appConfig.namespace || defaultNamespace
|
||||||
@ -142,7 +142,7 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => {
|
|||||||
callback(undefined, data)
|
callback(undefined, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err && err.body) {
|
if (err?.body) {
|
||||||
parseXML
|
parseXML
|
||||||
? xml2json(err.body, (error, result) => {
|
? xml2json(err.body, (error, result) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -173,7 +173,7 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => {
|
|||||||
: success(err.body)
|
: success(err.body)
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if (value && value[0] && value[1]) {
|
} else if (value?.[0] && value?.[1]) {
|
||||||
let messageCall
|
let messageCall
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
messageCall = value[1]
|
messageCall = value[1]
|
||||||
|
@ -536,10 +536,10 @@ const getSunstoneAuth = () => {
|
|||||||
const getDataZone = (zone = '0', configuredZones) => {
|
const getDataZone = (zone = '0', configuredZones) => {
|
||||||
let rtn
|
let rtn
|
||||||
const zones = global?.zones || configuredZones
|
const zones = global?.zones || configuredZones
|
||||||
if (zones && Array.isArray(zones)) {
|
if (Array.isArray(zones)) {
|
||||||
rtn = zones[0]
|
rtn = zones[0]
|
||||||
if (Number.isInteger(parseInt(zone, 10))) {
|
if (Number.isInteger(parseInt(zone, 10))) {
|
||||||
rtn = zones.find((zn) => zn && zn.id && String(zn.id) === String(zone))
|
rtn = zones.find((zn) => zn?.id && String(zn.id) === String(zone))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user