From d643dcdc81750f4d734c971571704b2c0af638fd Mon Sep 17 00:00:00 2001 From: Angel Date: Mon, 3 Feb 2025 17:24:20 +0100 Subject: [PATCH] F OpenNebula/one#6786: Fix issue creating Security Group (#3404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tino Vázquez --- .../components/Cards/SecurityGroupCard.js | 90 +++++--- .../Forms/SecurityGroups/ChangeForm/index.js | 37 ++++ .../Forms/SecurityGroups/ChangeForm/schema.js | 42 ++++ .../components/Forms/SecurityGroups/index.js | 9 +- .../Forms/Service/PerformAction/schema.js | 8 +- .../components/Tables/SecurityGroups/table.js | 3 +- .../src/modules/components/Tables/styles.js | 2 +- .../components/Tabs/VNetwork/Address.js | 107 ---------- .../components/Tabs/VNetwork/Clusters.js | 80 ------- .../components/Tabs/VNetwork/Info/index.js | 171 --------------- .../Tabs/VNetwork/Info/information.js | 155 -------------- .../components/Tabs/VNetwork/Info/qos.js | 86 -------- .../Tabs/VNetwork/Leases/LeaseItem.js | 193 ----------------- .../components/Tabs/VNetwork/Leases/index.js | 200 ------------------ .../components/Tabs/VNetwork/Security.js | 123 ----------- .../components/Tabs/VNetwork/VRouters.js | 79 ------- .../modules/components/Tabs/VNetwork/index.js | 80 ------- .../modules/components/Tabs/Vn/Security.js | 97 +++++++-- .../src/modules/constants/translates.js | 3 + src/fireedge/src/modules/utils/index.js | 1 + src/fireedge/src/modules/utils/secgroups.js | 50 +++++ 21 files changed, 279 insertions(+), 1337 deletions(-) create mode 100644 src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/index.js create mode 100644 src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/schema.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Address.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Clusters.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Info/index.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Info/information.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Info/qos.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Leases/LeaseItem.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Leases/index.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/Security.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/VRouters.js delete mode 100644 src/fireedge/src/modules/components/Tabs/VNetwork/index.js create mode 100644 src/fireedge/src/modules/utils/secgroups.js diff --git a/src/fireedge/src/modules/components/Cards/SecurityGroupCard.js b/src/fireedge/src/modules/components/Cards/SecurityGroupCard.js index 6e099683c8..6b3c3b73fe 100644 --- a/src/fireedge/src/modules/components/Cards/SecurityGroupCard.js +++ b/src/fireedge/src/modules/components/Cards/SecurityGroupCard.js @@ -13,22 +13,33 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { memo, ReactElement, useMemo } from 'react' -import { useTheme, Typography } from '@mui/material' import PropTypes from 'prop-types' +import { memo, ReactElement, useMemo } from 'react' -import { User, Group, PcCheck, PcNoEntry, PcWarning } from 'iconoir-react' +import { Typography, useTheme } from '@mui/material' +import { Group, PcCheck, PcNoEntry, PcWarning, User } from 'iconoir-react' +import { css } from '@emotion/css' +import { Tr } from '@modules/components/HOC' import MultipleTags from '@modules/components/MultipleTags' import { rowStyles } from '@modules/components/Tables/styles' import { SecurityGroup, T } from '@ConstantsModule' -import { getColorFromString, getUniqueLabels } from '@ModelsModule' import { useAuth } from '@FeaturesModule' -import { Tr } from '@modules/components/HOC' +import { getColorFromString, getUniqueLabels } from '@ModelsModule' +import clsx from 'clsx' const getTotalOfResources = (resources) => [resources?.ID ?? []].flat().length || 0 +const useStyles = () => ({ + internalContainer: css({ + display: 'flex', + }), + actions: css({ + marginLeft: 'auto', + }), +}) + const SecurityGroupCard = memo( /** * @param {object} props - Props @@ -42,6 +53,8 @@ const SecurityGroupCard = memo( ({ securityGroup, rootProps, actions, onClickLabel, onDeleteLabel }) => { const theme = useTheme() const classes = useMemo(() => rowStyles(theme), [theme]) + + const internalClasses = useStyles() const { labels: userLabels } = useAuth() const { @@ -84,38 +97,45 @@ const SecurityGroupCard = memo( return (
-
-
- - {NAME} - +
+
+
+ + {NAME} + - -
-
- {`#${ID}`} - - - {` ${UNAME}`} - - - - {` ${GNAME}`} - - - - {` ${totalUpdatedVms}`} - - - - {` ${totalOutdatedVms}`} - - - - {` ${totalErrorVms}`} - + +
+
+ {`#${ID}`} + + + {` ${UNAME}`} + + + + {` ${GNAME}`} + + + + {` ${totalUpdatedVms}`} + + + + {` ${totalOutdatedVms}`} + + + + {` ${totalErrorVms}`} + +
- {actions &&
{actions}
} + + {actions && ( +
+ {actions} +
+ )}
) diff --git a/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/index.js b/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/index.js new file mode 100644 index 0000000000..cf4d741f51 --- /dev/null +++ b/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/index.js @@ -0,0 +1,37 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, 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 { createForm, bindSecGroupTemplate } from '@UtilsModule' +import { SCHEMA, FIELDS } from './schema' +import { jsonToXml } from '@ModelsModule' + +const ChangeSecurityGroup = createForm(SCHEMA, FIELDS, { + transformInitialValue: (secGroupId, schema) => { + const secGroup = Array.isArray(secGroupId) ? secGroupId : [secGroupId] + + return { + ...schema.cast({ secGroup }, { stripUnknown: true }), + } + }, + transformBeforeSubmit: (formData, vnet) => { + const { secgroups } = formData + + const newTemplate = bindSecGroupTemplate(vnet, secgroups) + + return jsonToXml(newTemplate) + }, +}) + +export default ChangeSecurityGroup diff --git a/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/schema.js b/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/schema.js new file mode 100644 index 0000000000..b732ee6c40 --- /dev/null +++ b/src/fireedge/src/modules/components/Forms/SecurityGroups/ChangeForm/schema.js @@ -0,0 +1,42 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, 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 { string, object, ObjectSchema, array } from 'yup' + +import { SecurityGroupsTable } from '@modules/components/Tables' +import { T, INPUT_TYPES } from '@ConstantsModule' +import { Field, getValidationFromFields } from '@UtilsModule' + +/** @type {Field} Security group field */ +const SECGROUP = { + name: 'secgroups', + label: T.SelectNewSecGroup, + type: INPUT_TYPES.TABLE, + Table: () => SecurityGroupsTable.Table, + singleSelect: false, + validation: array(string().trim()) + .required() + .default(() => undefined), + fieldProps: { + preserveState: true, + }, + grid: { md: 12 }, +} + +/** @type {Field[]} List of fields */ +export const FIELDS = [SECGROUP] + +/** @type {ObjectSchema} Schema */ +export const SCHEMA = object(getValidationFromFields(FIELDS)) diff --git a/src/fireedge/src/modules/components/Forms/SecurityGroups/index.js b/src/fireedge/src/modules/components/Forms/SecurityGroups/index.js index c5d997878c..5024d7d0a7 100644 --- a/src/fireedge/src/modules/components/Forms/SecurityGroups/index.js +++ b/src/fireedge/src/modules/components/Forms/SecurityGroups/index.js @@ -38,4 +38,11 @@ const CreateForm = (configProps) => const CommitForm = (configProps) => AsyncLoadForm({ formPath: 'SecurityGroups/CommitForm' }, configProps) -export { CloneForm, CreateForm, CommitForm } +/** + * @param {ConfigurationProps} configProps - Configuration + * @returns {ReactElement|CreateFormCallback} Asynchronous loaded form + */ +const ChangeForm = (configProps) => + AsyncLoadForm({ formPath: 'SecurityGroups/ChangeForm' }, configProps) + +export { CloneForm, CreateForm, CommitForm, ChangeForm } diff --git a/src/fireedge/src/modules/components/Forms/Service/PerformAction/schema.js b/src/fireedge/src/modules/components/Forms/Service/PerformAction/schema.js index 3355bd6f7b..3a841e6320 100644 --- a/src/fireedge/src/modules/components/Forms/Service/PerformAction/schema.js +++ b/src/fireedge/src/modules/components/Forms/Service/PerformAction/schema.js @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ObjectSchema, string } from 'yup' -import { getObjectSchemaFromFields, arrayToOptions, Field } from '@UtilsModule' import { + ARGS_TYPES, INPUT_TYPES, T, - VM_ACTIONS_WITH_SCHEDULE, VM_ACTIONS, - ARGS_TYPES, + VM_ACTIONS_WITH_SCHEDULE, } from '@ConstantsModule' +import { Field, arrayToOptions, getObjectSchemaFromFields } from '@UtilsModule' +import { ObjectSchema, string } from 'yup' import { getRequiredArgsByAction } from '@ModelsModule' diff --git a/src/fireedge/src/modules/components/Tables/SecurityGroups/table.js b/src/fireedge/src/modules/components/Tables/SecurityGroups/table.js index 222f421d06..0eb6c56445 100644 --- a/src/fireedge/src/modules/components/Tables/SecurityGroups/table.js +++ b/src/fireedge/src/modules/components/Tables/SecurityGroups/table.js @@ -36,6 +36,7 @@ const SecurityGroupsTable = (props) => { rootProps = {}, searchProps = {}, useQuery = SecurityGroupAPI.useGetSecGroupsQuery, + rowComponent, ...rest } = props ?? {} rootProps['data-cy'] ??= DEFAULT_DATA_CY @@ -85,7 +86,7 @@ const SecurityGroupsTable = (props) => { }, ] - const { component, header } = WrapperRow(SecurityGroupsRow) + const { component, header } = WrapperRow(rowComponent ?? SecurityGroupsRow) return ( ({ +export const rowStyles = ({ palette, typography, breakpoints } = {}) => ({ root: css({ padding: '0.8em', color: palette.text.primary, diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Address.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Address.js deleted file mode 100644 index c3b5f75984..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Address.js +++ /dev/null @@ -1,107 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, Stack } from '@mui/material' -import PropTypes from 'prop-types' -import { ReactElement } from 'react' - -import { VnAPI } from '@FeaturesModule' - -import { - AddAddressRangeAction, - DeleteAddressRangeAction, - UpdateAddressRangeAction, -} from '@modules/components/Buttons' -import AddressRangeCard from '@modules/components/Cards/AddressRangeCard' - -import { AddressRange, VN_ACTIONS } from '@ConstantsModule' - -const { ADD_AR, UPDATE_AR, DELETE_AR } = VN_ACTIONS - -/** - * Renders the list of address ranges from a Virtual Network. - * - * @param {object} props - Props - * @param {object} props.tabProps - Tab information - * @param {string[]} props.tabProps.actions - Actions tab - * @param {string} props.id - Virtual Network id - * @param {object} props.oneConfig - Open Nebula configuration - * @param {boolean} props.adminGroup - If the user belongs to oneadmin group - * @returns {ReactElement} AR tab - */ -const AddressTab = ({ - tabProps: { actions } = {}, - id, - oneConfig, - adminGroup, -}) => { - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - - /** @type {AddressRange[]} */ - const addressRanges = [vnet?.AR_POOL?.AR ?? []].flat() - - return ( - - {actions[ADD_AR] === true && ( - - )} - - - {addressRanges.map((ar) => ( - - {actions[UPDATE_AR] === true && ( - - )} - {actions[DELETE_AR] === true && ( - - )} - - } - /> - ))} - - - ) -} - -AddressTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, - oneConfig: PropTypes.object, - adminGroup: PropTypes.bool, -} - -AddressTab.displayName = 'AddressTab' - -export default AddressTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Clusters.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Clusters.js deleted file mode 100644 index daa4524094..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Clusters.js +++ /dev/null @@ -1,80 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, useMemo } from 'react' -import PropTypes from 'prop-types' - -import { useHistory } from 'react-router' -import { generatePath } from 'react-router-dom' -import { Box } from '@mui/material' - -import { useViews, ClusterAPI, VnAPI } from '@FeaturesModule' - -import { ClustersTable } from '@modules/components/Tables' -import { RESOURCE_NAMES } from '@ConstantsModule' -import { PATH } from '@modules/components/path' - -const { CLUSTER } = RESOURCE_NAMES - -/** - * Renders the list of clusters from a Virtual Network. - * - * @param {object} props - Props - * @param {string} props.id - Virtual Network id - * @returns {ReactElement} Clusters tab - */ -const ClustersTab = ({ id }) => { - const { push: redirectTo } = useHistory() - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - - const { view, hasAccessToResource } = useViews() - const detailAccess = useMemo(() => hasAccessToResource(CLUSTER), [view]) - - const clusters = [vnet?.CLUSTERS?.ID ?? []].flat().map((clId) => +clId) - - const redirectToCluster = (row) => { - const clusterPath = PATH.INFRASTRUCTURE.CLUSTERS.DETAIL - redirectTo(generatePath(clusterPath, { id: row.ID })) - } - - const useQuery = () => - ClusterAPI.useGetClustersQuery(undefined, { - selectFromResult: ({ data: result = [], ...rest }) => ({ - data: result?.filter((cluster) => clusters.includes(+cluster.ID)), - ...rest, - }), - }) - - return ( - - - - ) -} - -ClustersTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, -} - -ClustersTab.displayName = 'ClustersTab' - -export default ClustersTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/index.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Info/index.js deleted file mode 100644 index f4b869b574..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/index.js +++ /dev/null @@ -1,171 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, useCallback } from 'react' -import PropTypes from 'prop-types' -import { Stack } from '@mui/material' - -import { VnAPI } from '@FeaturesModule' -import { - Permissions, - Ownership, - AttributePanel, -} from '@modules/components/Tabs/Common' -import Information from '@modules/components/Tabs/VNetwork/Info/information' -import QOS from '@modules/components/Tabs/VNetwork/Info/qos' - -import { Tr } from '@modules/components/HOC' -import { T } from '@ConstantsModule' -import { getActionsAvailable, filterAttributes, jsonToXml } from '@ModelsModule' -import { cloneObject, set } from '@UtilsModule' - -const LXC_ATTRIBUTES_REG = /^LXC_/ -const HIDDEN_ATTRIBUTES_REG = - /^(ERROR|SECURITY_GROUPS|INBOUND_AVG_BW|INBOUND_PEAK_BW|INBOUND_PEAK_KB|OUTBOUND_AVG_BW|OUTBOUND_PEAK_BW|OUTBOUND_PEAK_KB)$/ - -/** - * Renders mainly information tab. - * - * @param {object} props - Props - * @param {object} props.tabProps - Tab information - * @param {string} props.id - Virtual network id - * @param {object} props.oneConfig - OpenNebula configuration - * @param {boolean} props.adminGroup - If the user is admin - * @returns {ReactElement} Information tab - */ -const VNetworkInfoTab = ({ tabProps = {}, id, oneConfig, adminGroup }) => { - const { - information_panel: informationPanel, - permissions_panel: permissionsPanel, - ownership_panel: ownershipPanel, - qos_panel: qosPanel, - attributes_panel: attributesPanel, - lxc_panel: lxcPanel, - } = tabProps - - const [changeOwnership] = VnAPI.useChangeVNetOwnershipMutation() - const [changePermissions] = VnAPI.useChangeVNetPermissionsMutation() - const [update] = VnAPI.useUpdateVNetMutation() - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - - const { UNAME, UID, GNAME, GID, PERMISSIONS, TEMPLATE } = vnet - - const { attributes, lxc: lxcAttributes } = filterAttributes(TEMPLATE, { - extra: { - lxc: LXC_ATTRIBUTES_REG, - }, - hidden: HIDDEN_ATTRIBUTES_REG, - }) - - const handleChangeOwnership = async (newOwnership) => { - await changeOwnership({ id, ...newOwnership }) - } - - const handleChangePermission = async (newPermission) => { - await changePermissions({ id, ...newPermission }) - } - - const handleAttributeInXml = async (path, newValue) => { - const newTemplate = cloneObject(TEMPLATE) - set(newTemplate, path, newValue) - - const xml = jsonToXml(newTemplate) - await update({ id, template: xml, replace: 0 }) - } - - const getActions = useCallback( - (actions) => getActionsAvailable(actions), - [getActionsAvailable] - ) - - const ATTRIBUTE_FUNCTION = { - handleAdd: handleAttributeInXml, - handleEdit: handleAttributeInXml, - handleDelete: handleAttributeInXml, - } - - return ( - - {informationPanel?.enabled && ( - - )} - {permissionsPanel?.enabled && ( - - )} - {ownershipPanel?.enabled && ( - - )} - {qosPanel?.enabled && } - {attributesPanel?.enabled && attributes && ( - - )} - {lxcPanel?.enabled && lxcAttributes && ( - - )} - - ) -} - -VNetworkInfoTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, - oneConfig: PropTypes.object, - adminGroup: PropTypes.bool, -} - -VNetworkInfoTab.displayName = 'VNetworkInfoTab' - -export default VNetworkInfoTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/information.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Info/information.js deleted file mode 100644 index b91981c1b5..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/information.js +++ /dev/null @@ -1,155 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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 { generatePath } from 'react-router-dom' -import { Stack } from '@mui/material' - -import { VnAPI } from '@FeaturesModule' - -import { StatusCircle, StatusChip } from '@modules/components/Status' -import { List } from '@modules/components/Tabs/Common' - -import { - levelLockToString, - stringToBoolean, - booleanToString, - getState, -} from '@ModelsModule' -import { - T, - VNetwork, - VN_ACTIONS, - RESTRICTED_ATTRIBUTES_TYPE, -} from '@ConstantsModule' -import { PATH } from '@modules/components/path' -import { isRestrictedAttributes } from '@UtilsModule' - -/** - * Renders mainly information tab. - * - * @param {object} props - Props - * @param {VNetwork} props.vnet - Virtual Network resource - * @param {string[]} props.actions - Available actions to information tab - * @param {object} props.oneConfig - Open Nebula configuration - * @param {boolean} props.adminGroup - If the user belongs to oneadmin group - * @returns {ReactElement} Information tab - */ -const InformationPanel = ({ vnet = {}, actions, oneConfig, adminGroup }) => { - const [rename] = VnAPI.useRenameVNetMutation() - const { - ID, - NAME, - PARENT_NETWORK_ID: parentId, - LOCK, - VLAN_ID, - VLAN_ID_AUTOMATIC, - OUTER_VLAN_ID, - OUTER_VLAN_ID_AUTOMATIC, - } = vnet - - const { data: parent } = VnAPI.useGetVNetworkQuery( - { id: parentId }, - { skip: !parentId } - ) - - const { name: stateName, color: stateColor } = getState(vnet) - - const handleRename = async (_, newName) => { - await rename({ id: ID, name: newName }) - } - - const info = [ - { name: T.ID, value: ID, dataCy: 'id' }, - { - name: T.Name, - value: NAME, - dataCy: 'name', - canEdit: - actions?.includes?.(VN_ACTIONS.RENAME) && - (adminGroup || - !isRestrictedAttributes( - 'NAME', - undefined, - oneConfig[RESTRICTED_ATTRIBUTES_TYPE.VNET] - )), - handleEdit: handleRename, - }, - parentId && { - name: T.ReservationParent, - value: `#${parentId} ${parent?.NAME ?? '--'}`, - link: - !Number.isNaN(+parentId) && - generatePath(PATH.NETWORK.VNETS.DETAIL, { id: parentId }), - dataCy: 'parent', - }, - { - name: T.State, - value: ( - - - - - ), - }, - { - name: T.Locked, - value: levelLockToString(LOCK?.LOCKED), - dataCy: 'locked', - }, - { - name: T.VlanId, - value: VLAN_ID || '-', - dataCy: 'vlan-id', - }, - { - name: T.AutomaticVlanId, - value: booleanToString(stringToBoolean(VLAN_ID_AUTOMATIC)), - dataCy: 'vlan-id-automatic', - }, - { - name: T.OuterVlanId, - value: OUTER_VLAN_ID || '-', - dataCy: 'outer-vlan-id', - }, - { - name: T.AutomaticOuterVlanId, - value: booleanToString(stringToBoolean(OUTER_VLAN_ID_AUTOMATIC)), - dataCy: 'outer-vlan-id-automatic', - }, - ].filter(Boolean) - - return ( - <> - - - ) -} - -InformationPanel.propTypes = { - vnet: PropTypes.object, - actions: PropTypes.arrayOf(PropTypes.string), - oneConfig: PropTypes.object, - adminGroup: PropTypes.bool, -} - -InformationPanel.displayName = 'InformationPanel' - -export default InformationPanel diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/qos.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Info/qos.js deleted file mode 100644 index 601fed4256..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Info/qos.js +++ /dev/null @@ -1,86 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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 '@modules/components/Tabs/Common' -import { T, VNetwork } from '@ConstantsModule' - -/** - * Renders mainly information tab. - * - * @param {object} props - Props - * @param {VNetwork} props.vnet - Virtual Network resource - * @returns {ReactElement} Information tab - */ -const QOSPanel = ({ vnet = {} }) => { - const { - INBOUND_AVG_BW, - INBOUND_PEAK_BW, - INBOUND_PEAK_KB, - OUTBOUND_AVG_BW, - OUTBOUND_PEAK_BW, - OUTBOUND_PEAK_KB, - } = vnet.TEMPLATE - - const inbound = [ - { - name: T.AverageBandwidth, - value: INBOUND_AVG_BW?.concat(' KBytes/s') ?? '-', - dataCy: 'inbound-avg', - }, - { - name: T.PeakBandwidth, - value: INBOUND_PEAK_BW?.concat(' KBytes/s') ?? '-', - dataCy: 'inbound-peak-bandwidth', - }, - { - name: T.PeakBurst, - value: INBOUND_PEAK_KB?.concat(' KBytes') ?? '-', - dataCy: 'inbound-peak', - }, - ] - - const outbound = [ - { - name: T.AverageBandwidth, - value: OUTBOUND_AVG_BW?.concat(' KBytes/s') ?? '-', - dataCy: 'outbound-avg', - }, - { - name: T.PeakBandwidth, - value: OUTBOUND_PEAK_BW?.concat(' KBytes/s') ?? '-', - dataCy: 'outbound-peak-bandwidth', - }, - { - name: T.PeakBurst, - value: OUTBOUND_PEAK_KB?.concat(' KBytes') ?? '-', - dataCy: 'outbound-peak', - }, - ] - - return ( - <> - - - - ) -} - -QOSPanel.propTypes = { vnet: PropTypes.object } -QOSPanel.displayName = 'QOSPanel' - -export default QOSPanel diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/LeaseItem.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/LeaseItem.js deleted file mode 100644 index eb1dad87a4..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/LeaseItem.js +++ /dev/null @@ -1,193 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, memo, Fragment } from 'react' -import PropTypes from 'prop-types' -import ReleaseIcon from 'iconoir-react/dist/PlayOutline' -import { Link as RouterLink, generatePath } from 'react-router-dom' -import { Stack, Link, Typography } from '@mui/material' - -import { useReleaseLeaseMutation } from '@FeaturesModule' - -import { SubmitButton } from '@modules/components/FormControl' -import { StatusCircle, StatusChip } from '@modules/components/Status' - -import { getAddressType, getLeaseState } from '@ModelsModule' -import { T, VN_ACTIONS, ARLease, LEASES_STATES_STR } from '@ConstantsModule' -import { PATH } from '@modules/components/path' - -const LEASE_TYPES = { VM: 'vm', NET: 'net', VR: 'vr' } - -const LeaseStatus = ({ state, type }) => { - const { name: stateName, color: stateColor } = getLeaseState( - state || - (type === LEASE_TYPES.NET - ? LEASES_STATES_STR.RESERVED - : LEASES_STATES_STR.VROUTER) - ) - - return ( - - - - - ) -} -LeaseStatus.propTypes = { - state: PropTypes.string, - type: PropTypes.oneOf(Object.values(LEASE_TYPES)), -} - -/** - * Renders the name of lease. - * - * @param {object} props - Props - * @param {string} props.id - Resource id - * @param {'vm'|'net'|'vr'} props.type - Resource type: VM, VNET or VR - * @returns {ReactElement} Lease column name - */ -const LeaseName = ({ id, type }) => { - const path = { - [LEASE_TYPES.VM]: PATH.INSTANCE.VMS.DETAIL, - [LEASE_TYPES.NET]: PATH.NETWORK.VNETS.DETAIL, - [LEASE_TYPES.VR]: PATH.INSTANCE.VROUTERS.LIST, - }[type] - - return ( - svg': { mr: '1em' }, - }} - > - {`${type.toUpperCase()}: ${id}`} - - ) -} - -LeaseName.propTypes = { - id: PropTypes.string, - type: PropTypes.oneOf(Object.values(LEASE_TYPES)), -} - -const LeaseItem = memo( - /** - * @param {object} props - Props - * @param {string} props.id - Virtual Network id - * @param {object} props.actions - Actions tab - * @param {ARLease} props.lease - Lease to render - * @param {string} props.state - States - * @param {Function} props.resetHoldState - Reset hold state mutation - * @returns {ReactElement} Lease component - */ - ({ id, actions, lease, state, resetHoldState }) => { - const [releaseLease, { isLoading: isReleasing }] = useReleaseLeaseMutation() - - /** @type {ARLease} */ - const { - IP, - MAC, - addr = IP || MAC, - IP6, - IP6_GLOBAL, - IP6_LINK, - IP6_ULA, - VM: vmId, - VNET: vnetId, - VROUTER: vrId, - } = lease - - const release = async () => { - const template = `LEASES = [ ${getAddressType(addr)} = ${addr} ]` - await releaseLease({ id, template }).unwrap() - await resetHoldState() - } - - const resType = - vmId >= 0 - ? LEASE_TYPES.VM - : vnetId >= 0 - ? LEASE_TYPES.NET - : LEASE_TYPES.VR - - const resId = { - [LEASE_TYPES.VM]: vmId, - [LEASE_TYPES.NET]: vnetId, - [LEASE_TYPES.VR]: vrId, - }[resType] - - return ( - - {+vmId === -1 ? ( - actions[VN_ACTIONS.RELEASE_LEASE] && ( - } - label={T.ReleaseIp} - /> - ) - ) : ( - - )} - - {[ - { text: IP, dataCy: 'ip' }, - { text: IP6, dataCy: 'ip6' }, - { text: MAC, dataCy: 'mac' }, - { text: IP6_GLOBAL, dataCy: 'ip6-global' }, - { text: IP6_LINK, dataCy: 'ip6-link' }, - { text: IP6_ULA, dataCy: 'ip6-ula' }, - ].map(({ text = '--', dataCy }) => ( - - {text} - - ))} - - ) - } -) - -LeaseItem.propTypes = { - lease: PropTypes.object, - actions: PropTypes.object, - id: PropTypes.string, - state: PropTypes.string, - resetHoldState: PropTypes.func, -} - -LeaseItem.displayName = 'LeaseItem' - -export default LeaseItem diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/index.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/index.js deleted file mode 100644 index f01ea6bbae..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Leases/index.js +++ /dev/null @@ -1,200 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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 { Box, Typography, TextField, Skeleton } from '@mui/material' - -import { VnAPI, useGeneralApi } from '@FeaturesModule' - -import { SubmitButton } from '@modules/components/FormControl' -import { Translate } from '@modules/components/HOC' -import LeaseItem from '@modules/components/Tabs/VNetwork/Leases/LeaseItem' - -import { getAddressType } from '@ModelsModule' -import { - T, - AddressRange, - VN_ACTIONS, - LEASES_STATES_STR, -} from '@ConstantsModule' - -const LEASES_COLUMNS = [ - T.Resource, - T.State, - 'IP', - 'IP6', - 'MAC', - 'IP6 GLOBAL', - 'IP6 LINK', - 'IP6 ULA', -] - -const flatStates = (vnet, nameState = '') => { - const vnets = vnet?.[nameState]?.ID - - return [vnets ? (Array.isArray(vnets) ? vnets : [vnets]) : []].flat() -} - -const fillState = (state, id, stateName) => (state[id] = stateName) - -/** - * Renders the list of total leases from a Virtual Network. - * - * @param {object} props - Props - * @param {object} props.tabProps - Tab information - * @param {string[]} props.tabProps.actions - Actions tab - * @param {string} props.id - Virtual Network id - * @returns {ReactElement} AR tab - */ -const LeasesTab = ({ tabProps: { actions } = {}, id }) => { - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - const { enqueueError } = useGeneralApi() - const states = { - '-1': LEASES_STATES_STR.HOLD, - } - - const [holdLease, { isLoading, isSuccess, reset, originalArgs }] = - VnAPI.useHoldLeaseMutation() - - const errorVms = flatStates(vnet, 'ERROR_VMS') - const outdatedVms = flatStates(vnet, 'OUTDATED_VMS') - const updatingVms = flatStates(vnet, 'UPDATING_VMS') - const updatedVms = flatStates(vnet, 'UPDATED_VMS') - - errorVms.forEach((idVm) => fillState(states, idVm, LEASES_STATES_STR.ERROR)) - outdatedVms.forEach((idVm) => - fillState(states, idVm, LEASES_STATES_STR.OUTDATED) - ) - updatingVms.forEach((idVm) => - fillState(states, idVm, LEASES_STATES_STR.UPDATING) - ) - updatedVms.forEach((idVm) => - fillState(states, idVm, LEASES_STATES_STR.UPDATED) - ) - - /** @type {AddressRange[]} */ - const addressRanges = [vnet.AR_POOL.AR ?? []].flat() - const leases = addressRanges.map(({ LEASES }) => LEASES.LEASE ?? []).flat() - - const isHolding = - isLoading || - (isSuccess && - !leases.some( - (l) => - originalArgs?.template.includes(l.IP) || - originalArgs?.template.includes(l.IP6) || - originalArgs?.template.includes(l.MAC) - )) - - const hold = async (event) => { - try { - event.preventDefault() - const { addr } = Object.fromEntries(new FormData(event.target)) - const addrName = getAddressType(addr) - - if (!addrName) return enqueueError(T.SomethingWrong) - - const leasesToHold = `LEASES = [ ${addrName} = ${addr} ]` - await holdLease({ id, template: leasesToHold }).unwrap() - } catch {} - } - - return ( - - {actions[VN_ACTIONS.HOLD_LEASE] === true && ( - - - - - )} - - span': { - // header styles - borderBottom: (theme) => `1px solid ${theme.palette.divider}`, - marginLeft: '-8px', - paddingLeft: '8px', - alignSelf: 'end', - }, - }} - > - {LEASES_COLUMNS.map((col, index) => ( - - - - ))} - {leases.map((lease) => ( - - ))} - - {isHolding && } - - ) -} - -LeasesTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, -} - -LeasesTab.displayName = 'LeasesTab' - -export default LeasesTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/Security.js b/src/fireedge/src/modules/components/Tabs/VNetwork/Security.js deleted file mode 100644 index f7f4d76bbd..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/Security.js +++ /dev/null @@ -1,123 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, useMemo } from 'react' -import PropTypes from 'prop-types' - -import AddIcon from 'iconoir-react/dist/AddCircledOutline' -import { useHistory } from 'react-router' -import { generatePath } from 'react-router-dom' -import { Box } from '@mui/material' - -import { useViews, SecurityGroupAPI, VnAPI } from '@FeaturesModule' - -import { SecurityGroupsTable, GlobalAction } from '@modules/components/Tables' -import { T, VN_ACTIONS, RESOURCE_NAMES } from '@ConstantsModule' -import { PATH } from '@modules/components/path' - -import { isRestrictedAttributes } from '@UtilsModule' - -const { SEC_GROUP } = RESOURCE_NAMES -const { ADD_SECGROUP } = VN_ACTIONS - -/** - * Renders the list of security groups from a Virtual Network. - * - * @param {object} props - Props - * @param {object} props.tabProps - Tab information - * @param {string[]} props.tabProps.actions - Actions tab - * @param {string} props.id - Virtual Network id - * @param {object} props.oneConfig - OpenNebula configuration - * @param {boolean} props.adminGroup - If the user belongs to the oneadmin group - * @returns {ReactElement} Security Groups tab - */ -const SecurityTab = ({ - tabProps: { actions } = {}, - id, - oneConfig, - adminGroup, -}) => { - const { push: redirectTo } = useHistory() - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - - const { view, hasAccessToResource } = useViews() - const detailAccess = useMemo(() => hasAccessToResource(SEC_GROUP), [view]) - - const splittedSecGroups = vnet?.TEMPLATE.SECURITY_GROUPS?.split(',') ?? [] - const secGroups = [splittedSecGroups].flat().map((sgId) => +sgId) - - const redirectToSecGroup = (row) => { - redirectTo(generatePath(PATH.NETWORK.SEC_GROUPS.DETAIL, { id: row.ID })) - } - - const useQuery = () => - SecurityGroupAPI.useGetSecGroupsQuery(undefined, { - selectFromResult: ({ data: result = [], ...rest }) => ({ - data: result?.filter((secgroup) => secGroups.includes(+secgroup.ID)), - ...rest, - }), - }) - - /** @type {GlobalAction[]} */ - const globalActions = - adminGroup || - !isRestrictedAttributes( - 'SECURITY_GROUPS', - undefined, - oneConfig?.VNET_RESTRICTED_ATTR - ) - ? [ - actions[ADD_SECGROUP] && { - accessor: VN_ACTIONS.ADD_SECGROUP, - dataCy: VN_ACTIONS.ADD_SECGROUP, - tooltip: T.SecurityGroup, - icon: AddIcon, - options: [ - { - dialogProps: { title: T.SecurityGroup }, - form: undefined, - onSubmit: () => async (formData) => { - console.log({ formData }) - }, - }, - ], - }, - ].filter(Boolean) - : undefined - - return ( - - - - ) -} - -SecurityTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, - oneConfig: PropTypes.object, - adminGroup: PropTypes.bool, -} - -SecurityTab.displayName = 'SecurityTab' - -export default SecurityTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/VRouters.js b/src/fireedge/src/modules/components/Tabs/VNetwork/VRouters.js deleted file mode 100644 index 21813e8bc9..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/VRouters.js +++ /dev/null @@ -1,79 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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, useMemo } from 'react' -import PropTypes from 'prop-types' - -import { useHistory } from 'react-router' -import { generatePath } from 'react-router-dom' -import { Box } from '@mui/material' - -import { useViews, VrAPI, VnAPI } from '@FeaturesModule' - -import { VRoutersTable } from '@modules/components/Tables' -import { RESOURCE_NAMES } from '@ConstantsModule' -import { PATH } from '@modules/components/path' - -const { VROUTER } = RESOURCE_NAMES - -/** - * Renders the list of virtual routers from a Virtual Network. - * - * @param {object} props - Props - * @param {string} props.id - Virtual Network id - * @returns {ReactElement} Virtual routers tab - */ -const VRoutersTab = ({ id }) => { - const { push: redirectTo } = useHistory() - const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - - const { view, hasAccessToResource } = useViews() - const detailAccess = useMemo(() => hasAccessToResource(VROUTER), [view]) - - const vrouters = [vnet?.VROUTERS?.ID ?? []].flat().map((vrId) => +vrId) - - const redirectToVRouter = (row) => { - redirectTo(generatePath(PATH.INSTANCE.VROUTERS.DETAIL, { id: row.ID })) - } - - const useQuery = () => - VrAPI.useGetVrsQuery(undefined, { - selectFromResult: ({ data: result = [], ...rest }) => ({ - data: result?.filter((vrouter) => vrouters.includes(+vrouter.ID)), - ...rest, - }), - }) - - return ( - - - - ) -} - -VRoutersTab.propTypes = { - tabProps: PropTypes.object, - id: PropTypes.string, -} - -VRoutersTab.displayName = 'VRoutersTab' - -export default VRoutersTab diff --git a/src/fireedge/src/modules/components/Tabs/VNetwork/index.js b/src/fireedge/src/modules/components/Tabs/VNetwork/index.js deleted file mode 100644 index 41513bbc84..0000000000 --- a/src/fireedge/src/modules/components/Tabs/VNetwork/index.js +++ /dev/null @@ -1,80 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2024, 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 '@ConstantsModule' -import { useViews, useSystemData, VnAPI } from '@FeaturesModule' -import { getAvailableInfoTabs } from '@ModelsModule' - -import Tabs from '@modules/components/Tabs' -import Address from '@modules/components/Tabs/VNetwork/Address' -import Clusters from '@modules/components/Tabs/VNetwork/Clusters' -import Info from '@modules/components/Tabs/VNetwork/Info' -import Lease from '@modules/components/Tabs/VNetwork/Leases' -import Security from '@modules/components/Tabs/VNetwork/Security' -import VRouters from '@modules/components/Tabs/VNetwork/VRouters' - -const getTabComponent = (tabName) => - ({ - info: Info, - address: Address, - lease: Lease, - security: Security, - virtual_router: VRouters, - cluster: Clusters, - }[tabName]) - -const VNetworkTabs = memo(({ id }) => { - const { view, getResourceView } = useViews() - const { isError, error, status, data } = VnAPI.useGetVNetworkQuery({ - id, - }) - - const { adminGroup, oneConfig } = useSystemData() - - const tabsAvailable = useMemo(() => { - const resource = RESOURCE_NAMES.VNET - const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {} - - return getAvailableInfoTabs( - infoTabs, - getTabComponent, - id, - oneConfig, - adminGroup - ) - }, [view, id]) - - if (isError) { - return ( - - {error.data} - - ) - } - - if (status === 'fulfilled' || id === data?.ID) { - return - } - - return -}) -VNetworkTabs.propTypes = { id: PropTypes.string.isRequired } -VNetworkTabs.displayName = 'VNetworkTabs' - -export default VNetworkTabs diff --git a/src/fireedge/src/modules/components/Tabs/Vn/Security.js b/src/fireedge/src/modules/components/Tabs/Vn/Security.js index 0775529cd5..656f56039b 100644 --- a/src/fireedge/src/modules/components/Tabs/Vn/Security.js +++ b/src/fireedge/src/modules/components/Tabs/Vn/Security.js @@ -13,25 +13,79 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { ReactElement, useMemo } from 'react' import PropTypes from 'prop-types' +import { ReactElement } from 'react' -import AddIcon from 'iconoir-react/dist/AddCircledOutline' -import { useHistory } from 'react-router' -import { generatePath } from 'react-router-dom' import { Box } from '@mui/material' +import { + AddCircledOutline as AddIcon, + Trash as DeleteIcon, +} from 'iconoir-react/dist' -import { useViews, SecurityGroupAPI, VnAPI } from '@FeaturesModule' +import { SecurityGroupAPI, VnAPI, useGeneralApi } from '@FeaturesModule' -import { SecurityGroupsTable, GlobalAction } from '@modules/components/Tables' -import { T, VN_ACTIONS, RESOURCE_NAMES } from '@ConstantsModule' -import { PATH } from '@modules/components' +import { T, VN_ACTIONS } from '@ConstantsModule' +import { GlobalAction, SecurityGroupsTable } from '@modules/components/Tables' -import { isRestrictedAttributes } from '@UtilsModule' +import { isRestrictedAttributes, unbindSecGroupTemplate } from '@UtilsModule' + +import { ChangeForm } from '@modules/components/Forms/SecurityGroups' + +import { SecurityGroupCard } from '@modules/components/Cards' +import { SubmitButton } from '@modules/components/FormControl' + +import { jsonToXml } from '@ModelsModule' -const { SEC_GROUP } = RESOURCE_NAMES const { ADD_SECGROUP } = VN_ACTIONS +const RowComponent = ({ secgroup, vnet, extra }) => { + const { + headerList, + onClickLabel, + rowDataCy, + isSelected, + toggleRowSelected, + ...rest + } = extra + const [update] = VnAPI.useUpdateVNetMutation() + + const { enqueueSuccess } = useGeneralApi() + + return ( + } + onClick={async (evt) => { + evt.stopPropagation() + + const newTemplate = unbindSecGroupTemplate(vnet, secgroup) + + const xml = jsonToXml(newTemplate) + + const response = await update({ + id: vnet.ID, + template: xml, + }) + response && enqueueSuccess(T.UnbindSecurityGroupSuccess) + }} + /> + } + /> + ) +} + +RowComponent.propTypes = { + secgroup: PropTypes.object, + vnet: PropTypes.object, + extra: PropTypes.object, +} + +RowComponent.displayName = 'SecurityTab' + /** * Renders the list of security groups from a Virtual Network. * @@ -49,18 +103,13 @@ const SecurityTab = ({ oneConfig, adminGroup, }) => { - const { push: redirectTo } = useHistory() const { data: vnet } = VnAPI.useGetVNetworkQuery({ id }) - const { view, hasAccessToResource } = useViews() - const detailAccess = useMemo(() => hasAccessToResource(SEC_GROUP), [view]) - + const [update] = VnAPI.useUpdateVNetMutation() const splittedSecGroups = vnet?.TEMPLATE.SECURITY_GROUPS?.split(',') ?? [] const secGroups = [splittedSecGroups].flat().map((sgId) => +sgId) - const redirectToSecGroup = (row) => { - redirectTo(generatePath(PATH.NETWORK.SEC_GROUPS.DETAIL, { id: row.ID })) - } + const { enqueueSuccess } = useGeneralApi() const useQuery = () => SecurityGroupAPI.useGetSecGroupsQuery(undefined, { @@ -87,9 +136,13 @@ const SecurityTab = ({ options: [ { dialogProps: { title: T.SecurityGroup }, - form: undefined, - onSubmit: () => async (formData) => { - console.log({ formData }) + form: () => ChangeForm({ initialValues: vnet }), + onSubmit: () => async (xml) => { + const response = await update({ + id: vnet.ID, + template: xml, + }) + response && enqueueSuccess(T.BindSecurityGroupSuccess) }, }, ], @@ -103,7 +156,9 @@ const SecurityTab = ({ disableGlobalSort disableRowSelect pageSize={5} - onRowClick={detailAccess ? redirectToSecGroup : undefined} + rowComponent={({ original: secgroup, handleClick: _, ...props }) => ( + + )} globalActions={globalActions} useQuery={useQuery} /> diff --git a/src/fireedge/src/modules/constants/translates.js b/src/fireedge/src/modules/constants/translates.js index e0224680e7..bd72cfc16f 100644 --- a/src/fireedge/src/modules/constants/translates.js +++ b/src/fireedge/src/modules/constants/translates.js @@ -206,6 +206,7 @@ module.exports = { SelectNetwork: 'Select a network', SelectVirtualNetworks: 'Select virtual networks', SelectNewCluster: 'Select a new Cluster', + SelectNewSecGroup: 'Select security group', SelectRequest: 'Select request', SelectTheNewDatastore: 'Select the new datastore', SelectTheNewGroup: 'Select the new group', @@ -650,6 +651,8 @@ module.exports = { NetworksTopologies: 'Networks topologies', SecurityGroup: 'Security group', SecurityGroups: 'Security groups', + UnbindSecurityGroupSuccess: 'Security group deleted from the VNET', + BindSecurityGroupSuccess: 'Security group added succesfully to the VNET', NoNetworksInMonitoring: 'There is currently no network monitoring information associated with this VM', diff --git a/src/fireedge/src/modules/utils/index.js b/src/fireedge/src/modules/utils/index.js index 3458c4e1d4..f3561364ed 100644 --- a/src/fireedge/src/modules/utils/index.js +++ b/src/fireedge/src/modules/utils/index.js @@ -28,3 +28,4 @@ export * from '@modules/utils/translation' export * from '@modules/utils/units' export * from '@modules/utils/restrictedAttributes' export * from '@modules/utils/tabManifest' +export * from '@modules/utils/secgroups' diff --git a/src/fireedge/src/modules/utils/secgroups.js b/src/fireedge/src/modules/utils/secgroups.js new file mode 100644 index 0000000000..de025e59b8 --- /dev/null +++ b/src/fireedge/src/modules/utils/secgroups.js @@ -0,0 +1,50 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2024, 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. * + * ------------------------------------------------------------------------- */ +/** + * Unbind security group from VNET. + * + * @param {object} vnet - VNET. + * @param {object} secgroup - Security group. + * @returns {object} - Object from wich XML will be created. + */ +export const unbindSecGroupTemplate = (vnet, secgroup) => { + const splittedSecGroups = vnet?.TEMPLATE.SECURITY_GROUPS?.split(',') ?? [] + const currentSecGroups = [splittedSecGroups].flat().map((sgId) => +sgId) + + const secGroupsUpdated = currentSecGroups.filter((id) => id !== +secgroup.ID) + + return { ...vnet.TEMPLATE, SECURITY_GROUPS: secGroupsUpdated.join(',') } +} + +/** + * Bind security group to VNET. + * + * @param {object} vnet - VNET. + * @param {object} secgroups - Security group. + * @returns {object} - Object from wich XML will be created. + */ +export const bindSecGroupTemplate = (vnet, secgroups) => { + const newSecGroup = secgroups.map((secGroup) => +secGroup) + + const splittedSecGroups = vnet?.TEMPLATE.SECURITY_GROUPS?.split(',') ?? [] + const currentSecGroups = [splittedSecGroups].flat().map((sgId) => +sgId) + + newSecGroup.forEach((newSec) => { + !currentSecGroups.includes(newSec) && currentSecGroups.push(newSec) + }) + + return { ...vnet.TEMPLATE, SECURITY_GROUPS: currentSecGroups.join(',') } +}