mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
parent
9b8487ec04
commit
e31c5deec0
@ -37,6 +37,7 @@ import {
|
||||
Home as SystemIcon,
|
||||
User as UserIcon,
|
||||
Group as GroupIcon,
|
||||
HistoricShield as SecurityGroupIcon,
|
||||
} from 'iconoir-react'
|
||||
|
||||
import loadable from '@loadable/component'
|
||||
@ -104,6 +105,18 @@ const Files = loadable(() => import('client/containers/Files'), {
|
||||
const CreateFiles = loadable(() => import('client/containers/Files/Create'), {
|
||||
ssr: false,
|
||||
})
|
||||
const SecurityGroups = loadable(
|
||||
() => import('client/containers/SecurityGroups'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
const CreateSecurityGroups = loadable(
|
||||
() => import('client/containers/SecurityGroups/Create'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
const CreateImages = loadable(() => import('client/containers/Images/Create'), {
|
||||
ssr: false,
|
||||
})
|
||||
@ -129,6 +142,10 @@ const VirtualNetworks = loadable(
|
||||
() => import('client/containers/VirtualNetworks'),
|
||||
{ ssr: false }
|
||||
)
|
||||
const VirtualNetworksDetail = loadable(
|
||||
() => import('client/containers/VirtualNetworks/Detail'),
|
||||
{ ssr: false }
|
||||
)
|
||||
const CreateVirtualNetwork = loadable(
|
||||
() => import('client/containers/VirtualNetworks/Create'),
|
||||
{ ssr: false }
|
||||
@ -138,7 +155,6 @@ const VNetworkTemplates = loadable(
|
||||
{ ssr: false }
|
||||
)
|
||||
// const NetworkTopologies = loadable(() => import('client/containers/NetworkTopologies'), { ssr: false })
|
||||
// const SecurityGroups = loadable(() => import('client/containers/SecurityGroups'), { ssr: false })
|
||||
|
||||
const Clusters = loadable(() => import('client/containers/Clusters'), {
|
||||
ssr: false,
|
||||
@ -236,6 +252,7 @@ export const PATH = {
|
||||
SEC_GROUPS: {
|
||||
LIST: `/${RESOURCE_NAMES.SEC_GROUP}`,
|
||||
DETAIL: `/${RESOURCE_NAMES.SEC_GROUP}/:id`,
|
||||
CREATE: `/${RESOURCE_NAMES.SEC_GROUP}/create`,
|
||||
},
|
||||
},
|
||||
INFRASTRUCTURE: {
|
||||
@ -434,6 +451,12 @@ const ENDPOINTS = [
|
||||
title: T.Networks,
|
||||
icon: NetworksIcon,
|
||||
routes: [
|
||||
{
|
||||
title: T.VirtualNetworks,
|
||||
description: (params) => `#${params?.id}`,
|
||||
path: PATH.NETWORK.VNETS.DETAIL,
|
||||
Component: VirtualNetworksDetail,
|
||||
},
|
||||
{
|
||||
title: T.VirtualNetworks,
|
||||
path: PATH.NETWORK.VNETS.LIST,
|
||||
@ -458,6 +481,18 @@ const ENDPOINTS = [
|
||||
path: PATH.NETWORK.VNETS.CREATE,
|
||||
Component: CreateVirtualNetwork,
|
||||
},
|
||||
{
|
||||
title: T.SecurityGroups,
|
||||
path: PATH.NETWORK.SEC_GROUPS.LIST,
|
||||
sidebar: true,
|
||||
icon: SecurityGroupIcon,
|
||||
Component: SecurityGroups,
|
||||
},
|
||||
{
|
||||
title: T.CreateSecurityGroup,
|
||||
path: PATH.NETWORK.SEC_GROUPS.CREATE,
|
||||
Component: CreateSecurityGroups,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -35,7 +35,8 @@ import MultipleTags from 'client/components/MultipleTags'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { stringToBoolean } from 'client/models/Helper'
|
||||
import { groupBy } from 'client/utils'
|
||||
import { T, Nic, NicAlias, PrettySecurityGroupRule } from 'client/constants'
|
||||
import { T, Nic, NicAlias } from 'client/constants'
|
||||
import { SecurityGroupRules } from 'client/components/Tabs/Common/RulesSecGroups'
|
||||
|
||||
const NicCard = memo(
|
||||
/**
|
||||
@ -210,92 +211,4 @@ NicCard.propTypes = {
|
||||
|
||||
NicCard.displayName = 'NicCard'
|
||||
|
||||
const SecurityGroupRules = memo(({ parentKey, id, actions, rules }) => {
|
||||
const classes = rowStyles()
|
||||
|
||||
const COLUMNS = useMemo(
|
||||
() => [T.Protocol, T.Type, T.Range, T.Network, T.IcmpType],
|
||||
[]
|
||||
)
|
||||
|
||||
const name = rules?.[0]?.NAME ?? 'default'
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<Typography
|
||||
noWrap
|
||||
component="span"
|
||||
variant="subtitle1"
|
||||
data-cy={`${parentKey}-rule-name`}
|
||||
>
|
||||
{`#${id} ${name}`}
|
||||
</Typography>
|
||||
{!!actions && <div className={classes.actions}>{actions}</div>}
|
||||
</Stack>
|
||||
<Box display="grid" gridTemplateColumns="repeat(5, 1fr)" gap="0.5em">
|
||||
{COLUMNS.map((col) => (
|
||||
<Typography
|
||||
key={`${parentKey}-${col}`}
|
||||
noWrap
|
||||
component="span"
|
||||
variant="subtitle2"
|
||||
>
|
||||
<Translate word={col} />
|
||||
</Typography>
|
||||
))}
|
||||
{rules.map((rule) => (
|
||||
<SecurityGroupRule
|
||||
key={`${parentKey}-rule-${rule.RULE_TYPE}`}
|
||||
data-cy={`${parentKey}-rule-${rule.RULE_TYPE}`}
|
||||
rule={rule}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
SecurityGroupRules.propTypes = {
|
||||
parentKey: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
rules: PropTypes.array,
|
||||
actions: PropTypes.node,
|
||||
}
|
||||
|
||||
SecurityGroupRules.displayName = 'SecurityGroupRule'
|
||||
|
||||
const SecurityGroupRule = memo(({ rule, 'data-cy': parentCy }) => {
|
||||
/** @type {PrettySecurityGroupRule} */
|
||||
const { PROTOCOL, RULE_TYPE, ICMP_TYPE, RANGE, NETWORK_ID } = rule
|
||||
|
||||
return (
|
||||
<>
|
||||
{[
|
||||
{ text: PROTOCOL, dataCy: 'protocol' },
|
||||
{ text: RULE_TYPE, dataCy: 'ruletype' },
|
||||
{ text: RANGE, dataCy: 'range' },
|
||||
{ text: NETWORK_ID, dataCy: 'networkid' },
|
||||
{ text: ICMP_TYPE, dataCy: 'icmp-type' },
|
||||
].map(({ text, dataCy }) => (
|
||||
<Typography
|
||||
noWrap
|
||||
key={`${parentCy}-${dataCy}`}
|
||||
data-cy={`${parentCy}-${dataCy}`.toLowerCase()}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
SecurityGroupRule.propTypes = {
|
||||
rule: PropTypes.object,
|
||||
'data-cy': PropTypes.string,
|
||||
}
|
||||
|
||||
SecurityGroupRule.displayName = 'SecurityGroupRule'
|
||||
|
||||
export default NicCard
|
||||
|
@ -0,0 +1,24 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 } from 'client/utils'
|
||||
import {
|
||||
SCHEMA,
|
||||
FIELDS,
|
||||
} from 'client/components/Forms/SecurityGroups/CloneForm/schema'
|
||||
|
||||
const cloneSecGroupForm = createForm(SCHEMA, FIELDS)
|
||||
|
||||
export default cloneSecGroupForm
|
@ -0,0 +1,52 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 } from 'yup'
|
||||
|
||||
import { Field, getValidationFromFields } from 'client/utils'
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
|
||||
const PREFIX = {
|
||||
name: 'prefix',
|
||||
label: T.Prefix,
|
||||
tooltip: T.PrefixSecGroupsMultipleConcept,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => T.CopyOf),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
const SEC_GROUP = {
|
||||
name: 'name',
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => ''),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} [stepProps] - Step props
|
||||
* @param {boolean} [stepProps.isMultiple]
|
||||
* - If true, the prefix will be added to the name of the new template
|
||||
* @returns {Field[]} Fields
|
||||
*/
|
||||
export const FIELDS = ({ isMultiple } = {}) => [isMultiple ? PREFIX : SEC_GROUP]
|
||||
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS()))
|
@ -0,0 +1,49 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 {
|
||||
SCHEMA,
|
||||
FIELDS,
|
||||
} from 'client/components/Forms/SecurityGroups/CreateForm/Steps/General/schema'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'general'
|
||||
|
||||
const Content = () => (
|
||||
<FormWithSchema id={STEP_ID} fields={FIELDS} cy={`${STEP_ID}`} />
|
||||
)
|
||||
|
||||
/**
|
||||
* General configuration about Security Groups.
|
||||
*
|
||||
* @param {object} securityGroupData - security group data
|
||||
* @returns {object} Security Groups configuration step
|
||||
*/
|
||||
const General = (securityGroupData = {}) => {
|
||||
const isUpdate = securityGroupData?.NAME
|
||||
|
||||
return {
|
||||
id: STEP_ID,
|
||||
label: T.Configuration,
|
||||
resolver: (formdata) => SCHEMA(isUpdate),
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: () => Content(securityGroupData),
|
||||
}
|
||||
}
|
||||
|
||||
export default General
|
@ -0,0 +1,56 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 } from 'yup'
|
||||
import { Field, getValidationFromFields } from 'client/utils'
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
|
||||
export const IMAGE_LOCATION_TYPES = {
|
||||
PATH: 'path',
|
||||
UPLOAD: 'upload',
|
||||
}
|
||||
|
||||
/** @type {Field} name field */
|
||||
export const NAME = (isUpdate) => ({
|
||||
name: 'NAME',
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string().trim().required(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
...(isUpdate && { fieldProps: { disabled: true } }),
|
||||
})
|
||||
|
||||
/** @type {Field} Description field */
|
||||
export const DESCRIPTION = {
|
||||
name: 'DESCRIPTION',
|
||||
label: T.Description,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
multiline: true,
|
||||
validation: string().trim(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} isUpdate - is update.
|
||||
* @returns {Field[]} Fields
|
||||
*/
|
||||
export const FIELDS = (isUpdate) => [NAME(isUpdate), DESCRIPTION]
|
||||
|
||||
/**
|
||||
* @param {boolean} isUpdate - is update.
|
||||
* @returns {ObjectSchema} Schema
|
||||
*/
|
||||
export const SCHEMA = (isUpdate) =>
|
||||
object(getValidationFromFields(FIELDS(isUpdate)))
|
@ -0,0 +1,43 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 { array, object } from 'yup'
|
||||
|
||||
import RulesSection from './rulesSection'
|
||||
|
||||
import { SCHEMA } from 'client/components/Forms/SecurityGroups/CreateForm/Steps/Rules/schema'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'rules'
|
||||
|
||||
const Content = () => <RulesSection stepId={STEP_ID} />
|
||||
|
||||
/**
|
||||
* Rules configuration about Security Groups.
|
||||
*
|
||||
* @returns {object} Rules configuration step
|
||||
*/
|
||||
const Rules = () => ({
|
||||
id: STEP_ID,
|
||||
label: T.Rules,
|
||||
resolver: object({
|
||||
RULES: array(SCHEMA),
|
||||
}),
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: Content,
|
||||
})
|
||||
|
||||
export default Rules
|
@ -0,0 +1,213 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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, memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { Stack, FormControl, Link, Button, IconButton } from '@mui/material'
|
||||
import Table from '@mui/material/Table'
|
||||
import TableBody from '@mui/material/TableBody'
|
||||
import TableCell from '@mui/material/TableCell'
|
||||
import TableContainer from '@mui/material/TableContainer'
|
||||
import TableHead from '@mui/material/TableHead'
|
||||
import TableRow from '@mui/material/TableRow'
|
||||
import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
|
||||
import { useFieldArray, useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
|
||||
import { FormWithSchema, Legend } from 'client/components/Forms'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import {
|
||||
FIELDS,
|
||||
SCHEMA,
|
||||
} from 'client/components/Forms/SecurityGroups/CreateForm/Steps/Rules/schema'
|
||||
import {
|
||||
T,
|
||||
ICMP_STRING,
|
||||
ICMP_V6_STRING,
|
||||
RESOURCE_NAMES,
|
||||
} from 'client/constants'
|
||||
|
||||
export const SECTION_ID = 'RULES'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
container: {
|
||||
marginTop: '3rem',
|
||||
},
|
||||
})
|
||||
|
||||
const RulesSection = memo(
|
||||
/**
|
||||
* @param {object} props - Props
|
||||
* @param {string} [props.stepId] - ID of the step the section belongs to
|
||||
* @returns {ReactElement} - Inputs section
|
||||
*/
|
||||
({ stepId }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const fields = useMemo(() => FIELDS, [])
|
||||
|
||||
const {
|
||||
fields: rules,
|
||||
append,
|
||||
remove,
|
||||
} = useFieldArray({
|
||||
name: useMemo(
|
||||
() => [stepId, SECTION_ID].filter(Boolean).join('.'),
|
||||
[stepId]
|
||||
),
|
||||
})
|
||||
|
||||
const getCyPath = useCallback(
|
||||
(cy) => [stepId, cy].filter(Boolean).join('-'),
|
||||
[stepId]
|
||||
)
|
||||
|
||||
const methods = useForm({
|
||||
defaultValues: {
|
||||
[SECTION_ID]: SCHEMA.default(),
|
||||
},
|
||||
resolver: yupResolver(SCHEMA),
|
||||
})
|
||||
|
||||
const onSubmit = (newRule) => {
|
||||
newRule?.RULES && delete newRule.RULES
|
||||
append(newRule)
|
||||
methods.reset()
|
||||
}
|
||||
|
||||
if (fields.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl component="fieldset" sx={{ width: '100%' }}>
|
||||
<Legend title={T.Rules} />
|
||||
<FormProvider {...methods}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="flex-start"
|
||||
gap="0.5rem"
|
||||
component="form"
|
||||
onSubmit={methods.handleSubmit(onSubmit)}
|
||||
>
|
||||
<FormWithSchema
|
||||
cy={getCyPath('rules')}
|
||||
fields={fields}
|
||||
rootProps={{ sx: { m: 0 } }}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
type="submit"
|
||||
color="secondary"
|
||||
startIcon={<AddCircledOutline />}
|
||||
data-cy={getCyPath('add-rules')}
|
||||
sx={{ mt: '1em' }}
|
||||
>
|
||||
<Translate word={T.Add} />
|
||||
</Button>
|
||||
</Stack>
|
||||
</FormProvider>
|
||||
<TableContainer className={classes.container}>
|
||||
<Table sx={{ minWidth: 650 }} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>{T.Protocol}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Type}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Range}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.Network}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.IcmpType}</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>{T.IcmpTypeV6}</b>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rules?.map(
|
||||
(
|
||||
{
|
||||
id,
|
||||
PROTOCOL,
|
||||
RULE_TYPE,
|
||||
RANGE = T.All,
|
||||
IP,
|
||||
SIZE,
|
||||
NETWORK_ID,
|
||||
ICMP_TYPE = T.Any,
|
||||
// eslint-disable-next-line camelcase
|
||||
ICMPv6_TYPE = T.Any,
|
||||
},
|
||||
index
|
||||
) => {
|
||||
let network = T.Any
|
||||
if (IP && SIZE) {
|
||||
network = `${T.Start}: ${IP}, ${T.Size}: ${SIZE}`
|
||||
} else if (!isNaN(NETWORK_ID)) {
|
||||
network = (
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/${RESOURCE_NAMES.VNET}/${NETWORK_ID}`}
|
||||
color="secondary"
|
||||
>
|
||||
{NETWORK_ID}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{PROTOCOL}</TableCell>
|
||||
<TableCell>{RULE_TYPE}</TableCell>
|
||||
<TableCell>{RANGE}</TableCell>
|
||||
<TableCell>{network}</TableCell>
|
||||
<TableCell>{ICMP_STRING[ICMP_TYPE] || ''}</TableCell>
|
||||
<TableCell>{ICMP_V6_STRING[ICMPv6_TYPE] || ''}</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton onClick={() => remove(index)}>
|
||||
<DeleteCircledOutline />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</FormControl>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
RulesSection.propTypes = {
|
||||
stepId: PropTypes.string,
|
||||
}
|
||||
|
||||
RulesSection.displayName = 'RulesSection'
|
||||
|
||||
export default RulesSection
|
@ -0,0 +1,247 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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, mixed } from 'yup'
|
||||
import {
|
||||
Field,
|
||||
arrayToOptions,
|
||||
getValidationFromFields,
|
||||
upperCaseFirst,
|
||||
} from 'client/utils'
|
||||
import { VNetworksTable } from 'client/components/Tables'
|
||||
import {
|
||||
T,
|
||||
RULE_TYPE_STRING,
|
||||
INPUT_TYPES,
|
||||
PROTOCOL_STRING,
|
||||
ICMP_STRING,
|
||||
ICMP_V6_STRING,
|
||||
} from 'client/constants'
|
||||
|
||||
/** @type {Field} Rule type field */
|
||||
export const RULE_TYPE = {
|
||||
name: 'RULE_TYPE',
|
||||
label: T.Traffic,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(Object.values(RULE_TYPE_STRING), {
|
||||
addEmpty: false,
|
||||
getText: (ruleType) => {
|
||||
switch (ruleType) {
|
||||
case RULE_TYPE_STRING.OUTBOUND:
|
||||
return T.Outbound
|
||||
case RULE_TYPE_STRING.INBOUND:
|
||||
return T.Inbound
|
||||
default:
|
||||
return upperCaseFirst(ruleType.toLowerCase())
|
||||
}
|
||||
},
|
||||
getValue: (ruleType) => ruleType,
|
||||
}),
|
||||
validation: string()
|
||||
.trim()
|
||||
.default(() => RULE_TYPE_STRING.OUTBOUND)
|
||||
.afterSubmit((value) => value.toLowerCase()),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Protocol field */
|
||||
export const PROTOCOL = {
|
||||
name: 'PROTOCOL',
|
||||
label: T.Protocol,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(Object.values(PROTOCOL_STRING), {
|
||||
addEmpty: false,
|
||||
getText: (protocol) => {
|
||||
switch (protocol) {
|
||||
case PROTOCOL_STRING.TCP:
|
||||
return T.TCP
|
||||
case PROTOCOL_STRING.UDP:
|
||||
return T.UDP
|
||||
case PROTOCOL_STRING.ICMP:
|
||||
return T.ICMP
|
||||
case PROTOCOL_STRING.ICMPV6:
|
||||
return T.ICMPV6
|
||||
case PROTOCOL_STRING.IPSEC:
|
||||
return T.IPSEC
|
||||
case PROTOCOL_STRING.ALL:
|
||||
return T.All
|
||||
default:
|
||||
return upperCaseFirst(protocol.toLowerCase())
|
||||
}
|
||||
},
|
||||
getValue: (protocol) => protocol,
|
||||
}),
|
||||
validation: string()
|
||||
.trim()
|
||||
.default(() => PROTOCOL_STRING.TCP),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} ICMP_TYPE field */
|
||||
export const ICMP_TYPE = {
|
||||
name: 'ICMP_TYPE',
|
||||
dependOf: PROTOCOL.name,
|
||||
label: T.ICMP,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
htmlType: (protocol) =>
|
||||
protocol !== PROTOCOL_STRING.ICMP && INPUT_TYPES.HIDDEN,
|
||||
values: arrayToOptions(Object.entries(ICMP_STRING), {
|
||||
addEmpty: false,
|
||||
getText: ([_, value]) => value,
|
||||
getValue: ([key]) => key,
|
||||
}),
|
||||
validation: mixed().when(PROTOCOL.name, {
|
||||
is: (protocol) => protocol === PROTOCOL_STRING.ICMP,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} ICMPv6 field */
|
||||
export const ICMPV6_TYPE = {
|
||||
name: 'ICMPv6_TYPE',
|
||||
dependOf: PROTOCOL.name,
|
||||
label: T.ICMPV6,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
htmlType: (protocol) =>
|
||||
protocol !== PROTOCOL_STRING.ICMPV6 && INPUT_TYPES.HIDDEN,
|
||||
values: arrayToOptions(Object.entries(ICMP_V6_STRING), {
|
||||
addEmpty: false,
|
||||
getText: ([_, value]) => value,
|
||||
getValue: ([key]) => key,
|
||||
}),
|
||||
validation: mixed().when(PROTOCOL.name, {
|
||||
is: (protocol) => protocol === PROTOCOL_STRING.ICMPV6,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} RANGE, TYPE field */
|
||||
export const RANGE_TYPE = {
|
||||
name: 'RANGE_TYPE',
|
||||
label: T.PortRange,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions([T.All, T.PortRange], {
|
||||
addEmpty: false,
|
||||
}),
|
||||
validation: string()
|
||||
.trim()
|
||||
.afterSubmit((value) => undefined),
|
||||
grid: { xs: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Range field */
|
||||
export const RANGE = {
|
||||
name: 'RANGE',
|
||||
dependOf: RANGE_TYPE.name,
|
||||
label: T.PortRange,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: (range) => range !== T.PortRange && INPUT_TYPES.HIDDEN,
|
||||
validation: mixed().when(RANGE_TYPE.name, {
|
||||
is: (protocol) => protocol === T.PortRange,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Target field */
|
||||
export const TARGET = {
|
||||
name: 'TARGET',
|
||||
label: T.TargetNetwork,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(
|
||||
[T.AnyNetwork, T.ManualNetwork, T.OpennebulaVirtualNetwork],
|
||||
{
|
||||
addEmpty: false,
|
||||
}
|
||||
),
|
||||
validation: string()
|
||||
.trim()
|
||||
.afterSubmit((value) => undefined),
|
||||
grid: { xs: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} IP field */
|
||||
export const IP = {
|
||||
name: 'IP',
|
||||
dependOf: TARGET.name,
|
||||
label: T.FirstIPIPv6Address,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: (range) => range !== T.ManualNetwork && INPUT_TYPES.HIDDEN,
|
||||
validation: mixed().when(TARGET.name, {
|
||||
is: (protocol) => protocol === T.ManualNetwork,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} SIZE field */
|
||||
export const SIZE = {
|
||||
name: 'SIZE',
|
||||
dependOf: TARGET.name,
|
||||
label: T.Size,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: (range) => range !== T.ManualNetwork && INPUT_TYPES.HIDDEN,
|
||||
validation: mixed().when(TARGET.name, {
|
||||
is: (protocol) => protocol === T.ManualNetwork,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field} Networks field */
|
||||
const NETWORK_ID = {
|
||||
name: 'NETWORK_ID',
|
||||
label: T.SelectNewNetwork,
|
||||
type: INPUT_TYPES.TABLE,
|
||||
dependOf: TARGET.name,
|
||||
htmlType: (range) =>
|
||||
range !== T.OpennebulaVirtualNetwork && INPUT_TYPES.HIDDEN,
|
||||
Table: () => VNetworksTable,
|
||||
validation: mixed().when(RANGE.name, {
|
||||
is: (protocol) => protocol === T.OpennebulaVirtualNetwork,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired().nullable(),
|
||||
}),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Field[]} Fields
|
||||
*/
|
||||
export const FIELDS = [
|
||||
RULE_TYPE,
|
||||
PROTOCOL,
|
||||
ICMP_TYPE,
|
||||
ICMPV6_TYPE,
|
||||
RANGE_TYPE,
|
||||
RANGE,
|
||||
TARGET,
|
||||
IP,
|
||||
SIZE,
|
||||
NETWORK_ID,
|
||||
]
|
||||
|
||||
/**
|
||||
* @param {object} [stepProps] - Step props
|
||||
* @returns {ObjectSchema} Schema
|
||||
*/
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,53 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 Rules, {
|
||||
STEP_ID as RULES_ID,
|
||||
} from 'client/components/Forms/SecurityGroups/CreateForm/Steps/Rules'
|
||||
|
||||
import General, {
|
||||
STEP_ID as GENERAL_ID,
|
||||
} from 'client/components/Forms/SecurityGroups/CreateForm/Steps/General'
|
||||
|
||||
import { createSteps } from 'client/utils'
|
||||
|
||||
const Steps = createSteps([General, Rules], {
|
||||
transformInitialValue: ({ TEMPLATE, ...secGroup } = {}, schema) =>
|
||||
schema.cast(
|
||||
{
|
||||
[GENERAL_ID]: { ...TEMPLATE, ...secGroup },
|
||||
[RULES_ID]: {
|
||||
RULES: Array.isArray(TEMPLATE.RULE) ? TEMPLATE.RULE : [TEMPLATE.RULE],
|
||||
},
|
||||
},
|
||||
{ stripUnknown: true, context: secGroup }
|
||||
),
|
||||
transformBeforeSubmit: (formData) => {
|
||||
const { [GENERAL_ID]: general = {}, [RULES_ID]: rules = {} } =
|
||||
formData ?? {}
|
||||
|
||||
const rtn = {
|
||||
template: {
|
||||
...general,
|
||||
},
|
||||
}
|
||||
|
||||
if (rules?.RULES) rtn.template.RULE = rules.RULES
|
||||
|
||||
return rtn
|
||||
},
|
||||
})
|
||||
|
||||
export default Steps
|
@ -0,0 +1,16 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 'client/components/Forms/SecurityGroups/CreateForm/Steps'
|
@ -0,0 +1,34 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 { CreateFormCallback } from 'client/utils/schema'
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
*/
|
||||
const CloneForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'SecurityGroups/CloneForm' }, configProps)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
*/
|
||||
const CreateForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'SecurityGroups/CreateForm' }, configProps)
|
||||
|
||||
export { CloneForm, CreateForm }
|
@ -0,0 +1,251 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { Typography } from '@mui/material'
|
||||
import { AddCircledOutline, Group, Trash } from 'iconoir-react'
|
||||
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import {
|
||||
useCloneSegGroupMutation,
|
||||
useChangeSecGroupOwnershipMutation,
|
||||
useRemoveSecGroupMutation,
|
||||
useCommitSegGroupMutation,
|
||||
} from 'client/features/OneApi/securityGroup'
|
||||
|
||||
import { ChangeUserForm, ChangeGroupForm } from 'client/components/Forms/Vm'
|
||||
import { CloneForm } from 'client/components/Forms/SecurityGroups'
|
||||
import {
|
||||
createActions,
|
||||
GlobalAction,
|
||||
} from 'client/components/Tables/Enhanced/Utils'
|
||||
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { T, SEC_GROUP_ACTIONS, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
const ListSecGroupNames = ({ rows = [] }) =>
|
||||
rows?.map?.(({ id, original }) => {
|
||||
const { ID, NAME } = original
|
||||
|
||||
return (
|
||||
<Typography
|
||||
key={`segGroup-${id}`}
|
||||
variant="inherit"
|
||||
component="span"
|
||||
display="block"
|
||||
>
|
||||
{`#${ID} ${NAME}`}
|
||||
</Typography>
|
||||
)
|
||||
})
|
||||
|
||||
const SubHeader = (rows) => <ListSecGroupNames rows={rows} />
|
||||
|
||||
const MessageToConfirmAction = (rows, message) => (
|
||||
<>
|
||||
<ListSecGroupNames rows={rows} />
|
||||
{message && (
|
||||
<p>
|
||||
<Translate word={message} />
|
||||
</p>
|
||||
)}
|
||||
<Translate word={T.DoYouWantProceed} />
|
||||
</>
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates the actions to operate resources on Security Groups table.
|
||||
*
|
||||
* @returns {GlobalAction} - Actions
|
||||
*/
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useViews()
|
||||
const [clone] = useCloneSegGroupMutation()
|
||||
const [changeOwnership] = useChangeSecGroupOwnershipMutation()
|
||||
const [deleteSecGroup] = useRemoveSecGroupMutation()
|
||||
const [commitSecGroup] = useCommitSegGroupMutation()
|
||||
|
||||
const resourcesView = getResourceView(RESOURCE_NAMES.SEC_GROUP)?.actions
|
||||
|
||||
const segGroupActions = useMemo(
|
||||
() =>
|
||||
createActions({
|
||||
filters: resourcesView,
|
||||
actions: [
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.CREATE_DIALOG,
|
||||
dataCy: `securityGroup_${SEC_GROUP_ACTIONS.CREATE_DIALOG}`,
|
||||
tooltip: T.Create,
|
||||
icon: AddCircledOutline,
|
||||
action: () => {
|
||||
history.push(PATH.NETWORK.SEC_GROUPS.CREATE)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.UPDATE_DIALOG,
|
||||
label: T.Update,
|
||||
tooltip: T.Update,
|
||||
selected: { max: 1 },
|
||||
color: 'secondary',
|
||||
action: (rows) => {
|
||||
const secGroups = rows?.[0]?.original ?? {}
|
||||
const path = PATH.NETWORK.SEC_GROUPS.CREATE
|
||||
|
||||
history.push(path, secGroups)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.CLONE,
|
||||
label: T.Clone,
|
||||
tooltip: T.Clone,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
options: [
|
||||
{
|
||||
dialogProps: {
|
||||
title: (rows) => {
|
||||
const isMultiple = rows?.length > 1
|
||||
const { ID, NAME } = rows?.[0]?.original ?? {}
|
||||
|
||||
return [
|
||||
Tr(isMultiple ? T.CloneSecGroups : T.CloneSecGroup),
|
||||
!isMultiple && `#${ID} ${NAME}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' - ')
|
||||
},
|
||||
dataCy: 'modal-clone',
|
||||
},
|
||||
form: (rows) => {
|
||||
const names = rows?.map(({ original }) => original?.NAME)
|
||||
const stepProps = { isMultiple: names.length > 1 }
|
||||
const initialValues = { name: `Copy of ${names?.[0]}` }
|
||||
|
||||
return CloneForm({ stepProps, initialValues })
|
||||
},
|
||||
onSubmit:
|
||||
(rows) =>
|
||||
async ({ prefix, name } = {}) => {
|
||||
const secGroups = rows?.map?.(
|
||||
({ original: { ID, NAME } = {} }) =>
|
||||
// overwrite all names with prefix+NAME
|
||||
({
|
||||
id: ID,
|
||||
name: prefix ? `${prefix} ${NAME}` : name,
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(secGroups.map(clone))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.COMMIT,
|
||||
label: T.Commit,
|
||||
tooltip: T.Commit,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Confirm,
|
||||
dataCy: `modal-${SEC_GROUP_ACTIONS.COMMIT}`,
|
||||
children: (rows) =>
|
||||
MessageToConfirmAction(rows, T.CommitMessageSecGroups),
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => commitSecGroup({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tooltip: T.Ownership,
|
||||
icon: Group,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
dataCy: 'securityGroup-ownership',
|
||||
options: [
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.CHANGE_OWNER,
|
||||
name: T.ChangeOwner,
|
||||
dialogProps: {
|
||||
title: T.ChangeOwner,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${SEC_GROUP_ACTIONS.CHANGE_OWNER}`,
|
||||
},
|
||||
form: ChangeUserForm,
|
||||
onSubmit: (rows) => async (newOwnership) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(
|
||||
ids.map((id) => changeOwnership({ id, ...newOwnership }))
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.CHANGE_GROUP,
|
||||
name: T.ChangeGroup,
|
||||
dialogProps: {
|
||||
title: T.ChangeGroup,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${SEC_GROUP_ACTIONS.CHANGE_GROUP}`,
|
||||
},
|
||||
form: ChangeGroupForm,
|
||||
onSubmit: (rows) => async (newOwnership) => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(
|
||||
ids.map((id) => changeOwnership({ id, ...newOwnership }))
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
accessor: SEC_GROUP_ACTIONS.DELETE,
|
||||
tooltip: T.Delete,
|
||||
icon: Trash,
|
||||
color: 'error',
|
||||
selected: { min: 1 },
|
||||
dataCy: `secGroups_${SEC_GROUP_ACTIONS.DELETE}`,
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Delete,
|
||||
dataCy: `modal-${SEC_GROUP_ACTIONS.DELETE}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => deleteSecGroup({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[view]
|
||||
)
|
||||
|
||||
return segGroupActions
|
||||
}
|
||||
|
||||
export default Actions
|
224
src/fireedge/src/client/components/Tabs/Common/RulesSecGroups.js
Normal file
224
src/fireedge/src/client/components/Tabs/Common/RulesSecGroups.js
Normal file
@ -0,0 +1,224 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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, ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { T, PrettySecurityGroupRule, RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
import {
|
||||
styled,
|
||||
List,
|
||||
ListItem,
|
||||
Typography,
|
||||
Paper,
|
||||
Stack,
|
||||
Box,
|
||||
Link,
|
||||
} from '@mui/material'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
const Title = styled(ListItem)(({ theme }) => ({
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
}))
|
||||
|
||||
const Item = styled(ListItem)(({ theme }) => ({
|
||||
gap: '1em',
|
||||
'& > *': {
|
||||
flex: '1 1 50%',
|
||||
overflow: 'hidden',
|
||||
minHeight: '100%',
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
}))
|
||||
|
||||
const useStyles = makeStyles({
|
||||
container: {
|
||||
gridColumn: '1 / -1',
|
||||
},
|
||||
item: {
|
||||
'& > *:first-child': {
|
||||
flex: '1 1 20%',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const RulesSecGroupsTable = memo(({ title, rules = [] }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Paper className={classes.container} variant="outlined">
|
||||
<List variant="outlined">
|
||||
{title && (
|
||||
<Title>
|
||||
{typeof title === 'string' ? (
|
||||
<Typography noWrap>{Tr(title)}</Typography>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
</Title>
|
||||
)}
|
||||
<Item>
|
||||
<SecurityGroupRules parentKey={''} rules={rules} />
|
||||
</Item>
|
||||
</List>
|
||||
</Paper>
|
||||
)
|
||||
})
|
||||
|
||||
RulesSecGroupsTable.propTypes = {
|
||||
title: PropTypes.any,
|
||||
rules: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
PROTOCOL: PropTypes.string,
|
||||
RULE_TYPE: PropTypes.string,
|
||||
RANGE: PropTypes.string,
|
||||
NETWORK: PropTypes.string,
|
||||
ICMP_TYPE: PropTypes.string,
|
||||
})
|
||||
),
|
||||
}
|
||||
|
||||
RulesSecGroupsTable.displayName = 'RulesSecGroupsTable'
|
||||
|
||||
export const SecurityGroupRules = memo(
|
||||
({ parentKey = '', id, actions, rules }) => {
|
||||
const classes = rowStyles()
|
||||
|
||||
const COLUMNS = useMemo(
|
||||
() => [T.Protocol, T.Type, T.Range, T.Network, T.IcmpType],
|
||||
[]
|
||||
)
|
||||
|
||||
const name = rules?.[0]?.NAME ?? 'default'
|
||||
|
||||
return (
|
||||
<>
|
||||
{id !== undefined && (
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<Typography
|
||||
noWrap
|
||||
component="span"
|
||||
variant="subtitle1"
|
||||
data-cy={`${parentKey}-rule-name`}
|
||||
>
|
||||
{`#${id} ${name}`}
|
||||
</Typography>
|
||||
|
||||
{!!actions && <div className={classes.actions}>{actions}</div>}
|
||||
</Stack>
|
||||
)}
|
||||
<Box display="grid" gridTemplateColumns="repeat(5, 1fr)" gap="0.5em">
|
||||
{COLUMNS.map((col) => (
|
||||
<Typography
|
||||
key={`${parentKey}-${col}`}
|
||||
noWrap
|
||||
component="span"
|
||||
variant="subtitle2"
|
||||
>
|
||||
<Translate word={col} />
|
||||
</Typography>
|
||||
))}
|
||||
{rules.map((rule) => (
|
||||
<SecurityGroupRule
|
||||
key={uuidv4()}
|
||||
data-cy={`${parentKey}-rule-${rule.RULE_TYPE}`}
|
||||
rule={rule}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
SecurityGroupRules.propTypes = {
|
||||
parentKey: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
rules: PropTypes.array,
|
||||
actions: PropTypes.node,
|
||||
}
|
||||
|
||||
SecurityGroupRules.displayName = 'SecurityGroupRule'
|
||||
|
||||
export const SecurityGroupRule = memo(({ rule = {}, 'data-cy': parentCy }) => {
|
||||
/** @type {PrettySecurityGroupRule} */
|
||||
const {
|
||||
PROTOCOL = '',
|
||||
RULE_TYPE = '',
|
||||
ICMP_TYPE = '',
|
||||
RANGE = T.All,
|
||||
NETWORK_ID = T.Any,
|
||||
} = rule
|
||||
|
||||
/**
|
||||
* @param {object} rule - rule.
|
||||
* @param {string} rule.text - rule text
|
||||
* @param {string} rule.dataCy - rule data-cy
|
||||
* @param {boolean} rule.link - rule link
|
||||
* @returns {ReactElement} rule line
|
||||
*/
|
||||
const renderLine = ({ text, dataCy, link }) => (
|
||||
<Typography
|
||||
noWrap
|
||||
key={`${parentCy}-${dataCy}`}
|
||||
data-cy={`${parentCy}-${dataCy}`.toLowerCase()}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{link && !isNaN(text) ? (
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/${RESOURCE_NAMES.VNET}/${text}`}
|
||||
color="secondary"
|
||||
>
|
||||
{text}
|
||||
</Link>
|
||||
) : (
|
||||
text
|
||||
)}
|
||||
</Typography>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{[
|
||||
{ text: String(PROTOCOL).toUpperCase(), dataCy: 'protocol' },
|
||||
{ text: String(RULE_TYPE).toUpperCase(), dataCy: 'ruletype' },
|
||||
{ text: String(RANGE).toUpperCase(), dataCy: 'range' },
|
||||
{
|
||||
text: String(NETWORK_ID).toUpperCase(),
|
||||
dataCy: 'networkid',
|
||||
link: true,
|
||||
},
|
||||
{ text: String(ICMP_TYPE).toUpperCase(), dataCy: 'icmp-type' },
|
||||
].map(renderLine)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
SecurityGroupRule.propTypes = {
|
||||
rule: PropTypes.object,
|
||||
'data-cy': PropTypes.string,
|
||||
}
|
||||
|
||||
SecurityGroupRule.displayName = 'SecurityGroupRule'
|
||||
|
||||
export default RulesSecGroupsTable
|
@ -17,7 +17,8 @@ import AttributePanel from 'client/components/Tabs/Common/AttributePanel'
|
||||
import List from 'client/components/Tabs/Common/List'
|
||||
import Ownership from 'client/components/Tabs/Common/Ownership'
|
||||
import Permissions from 'client/components/Tabs/Common/Permissions'
|
||||
import RulesSecGroupsTable from 'client/components/Tabs/Common/RulesSecGroups'
|
||||
|
||||
export * from 'client/components/Tabs/Common/Attribute'
|
||||
|
||||
export { AttributePanel, List, Ownership, Permissions }
|
||||
export { AttributePanel, List, Ownership, Permissions, RulesSecGroupsTable }
|
||||
|
@ -44,7 +44,7 @@ const VmsTab = ({ id }) => {
|
||||
displaySelectedRows
|
||||
host={image}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
noDataMessage={<EmptyTab label={T.NotVmsCurrenty} />}
|
||||
noDataMessage={<EmptyTab label={T.NotVmsCurrentyImage} />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,165 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 {
|
||||
useGetSecGroupQuery,
|
||||
useChangeSecGroupOwnershipMutation,
|
||||
useChangeSecGroupPermissionsMutation,
|
||||
useUpdateSecGroupMutation,
|
||||
} from 'client/features/OneApi/securityGroup'
|
||||
import {
|
||||
Permissions,
|
||||
Ownership,
|
||||
AttributePanel,
|
||||
RulesSecGroupsTable,
|
||||
} from 'client/components/Tabs/Common'
|
||||
import Information from 'client/components/Tabs/SecurityGroup/Info/information'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import {
|
||||
getActionsAvailable,
|
||||
jsonToXml,
|
||||
filterAttributes,
|
||||
} from 'client/models/Helper'
|
||||
import { cloneObject, set } from 'client/utils'
|
||||
|
||||
const HIDDEN_ATTRIBUTES = /^(RULE)$/
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {object} props.tabProps - Tab information
|
||||
* @param {string} props.id - Image id
|
||||
* @returns {ReactElement} Information tab
|
||||
*/
|
||||
const SecurityGroupInfoTab = ({ tabProps = {}, id }) => {
|
||||
const {
|
||||
information_panel: informationPanel,
|
||||
permissions_panel: permissionsPanel,
|
||||
ownership_panel: ownershipPanel,
|
||||
attributes_panel: attributesPanel,
|
||||
rules_panel: rulesPanel,
|
||||
} = tabProps
|
||||
|
||||
const [changeOwnership] = useChangeSecGroupOwnershipMutation()
|
||||
const [changePermissions] = useChangeSecGroupPermissionsMutation()
|
||||
const [update] = useUpdateSecGroupMutation()
|
||||
const { data: securityGroup } = useGetSecGroupQuery({ id })
|
||||
|
||||
const { UNAME, UID, GNAME, GID, PERMISSIONS, TEMPLATE } = securityGroup
|
||||
|
||||
const { attributes } = filterAttributes(TEMPLATE, {
|
||||
hidden: HIDDEN_ATTRIBUTES,
|
||||
})
|
||||
|
||||
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 (
|
||||
<Stack
|
||||
display="grid"
|
||||
gap="1em"
|
||||
gridTemplateColumns="repeat(auto-fit, minmax(49%, 1fr))"
|
||||
padding={{ sm: '0.8em' }}
|
||||
>
|
||||
{informationPanel?.enabled && (
|
||||
<Information
|
||||
securityGroup={securityGroup}
|
||||
actions={getActions(informationPanel?.actions)}
|
||||
/>
|
||||
)}
|
||||
{permissionsPanel?.enabled && (
|
||||
<Permissions
|
||||
actions={getActions(permissionsPanel?.actions)}
|
||||
handleEdit={handleChangePermission}
|
||||
ownerUse={PERMISSIONS.OWNER_U}
|
||||
ownerManage={PERMISSIONS.OWNER_M}
|
||||
ownerAdmin={PERMISSIONS.OWNER_A}
|
||||
groupUse={PERMISSIONS.GROUP_U}
|
||||
groupManage={PERMISSIONS.GROUP_M}
|
||||
groupAdmin={PERMISSIONS.GROUP_A}
|
||||
otherUse={PERMISSIONS.OTHER_U}
|
||||
otherManage={PERMISSIONS.OTHER_M}
|
||||
otherAdmin={PERMISSIONS.OTHER_A}
|
||||
/>
|
||||
)}
|
||||
{ownershipPanel?.enabled && (
|
||||
<Ownership
|
||||
actions={getActions(ownershipPanel?.actions)}
|
||||
handleEdit={handleChangeOwnership}
|
||||
userId={UID}
|
||||
userName={UNAME}
|
||||
groupId={GID}
|
||||
groupName={GNAME}
|
||||
/>
|
||||
)}
|
||||
{rulesPanel?.enabled && (
|
||||
<RulesSecGroupsTable
|
||||
title={Tr(T.SecurityGroup)}
|
||||
rules={
|
||||
Array.isArray(TEMPLATE?.RULE) ? TEMPLATE?.RULE : [TEMPLATE?.RULE]
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{attributesPanel?.enabled && (
|
||||
<AttributePanel
|
||||
{...ATTRIBUTE_FUNCTION}
|
||||
attributes={attributes}
|
||||
actions={getActions(attributesPanel?.actions)}
|
||||
title={Tr(T.Attributes)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
SecurityGroupInfoTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
}
|
||||
|
||||
SecurityGroupInfoTab.displayName = 'ImageInfoTab'
|
||||
|
||||
export default SecurityGroupInfoTab
|
@ -0,0 +1,69 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 { useRenameSecGroupMutation } from 'client/features/OneApi/securityGroup'
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
import { T, Image, SEC_GROUP_ACTIONS } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {Image} props.securityGroup - Security Group resource
|
||||
* @param {string[]} props.actions - Available actions to information tab
|
||||
* @returns {ReactElement} Information tab
|
||||
*/
|
||||
const InformationPanel = ({ securityGroup = {}, actions }) => {
|
||||
const [rename] = useRenameSecGroupMutation()
|
||||
|
||||
const { ID, NAME } = securityGroup
|
||||
|
||||
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?.(SEC_GROUP_ACTIONS.RENAME),
|
||||
handleEdit: handleRename,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<List
|
||||
title={T.Information}
|
||||
list={info}
|
||||
containerProps={{ sx: { gridRow: 'span 3' } }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
InformationPanel.propTypes = {
|
||||
securityGroup: PropTypes.object,
|
||||
actions: PropTypes.arrayOf(PropTypes.string),
|
||||
}
|
||||
|
||||
InformationPanel.displayName = 'InformationPanel'
|
||||
|
||||
export default InformationPanel
|
@ -0,0 +1,59 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 { T } from 'client/constants'
|
||||
import EmptyTab from 'client/components/Tabs/EmptyTab'
|
||||
import { useHistory, generatePath } from 'react-router-dom'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { useGetSecGroupQuery } from 'client/features/OneApi/securityGroup'
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
|
||||
/**
|
||||
* Renders mainly Vms tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.id - Image id
|
||||
* @returns {ReactElement} vms tab
|
||||
*/
|
||||
const VmsTab = ({ id }) => {
|
||||
const { data: secGroup = {} } = useGetSecGroupQuery({ id })
|
||||
const path = PATH.INSTANCE.VMS.DETAIL
|
||||
const history = useHistory()
|
||||
|
||||
const handleRowClick = (rowId) => {
|
||||
history.push(generatePath(path, { id: String(rowId) }))
|
||||
}
|
||||
|
||||
return (
|
||||
<VmsTable
|
||||
disableGlobalSort
|
||||
displaySelectedRows
|
||||
host={secGroup}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
noDataMessage={<EmptyTab label={T.NotVmsCurrentySecGroups} />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
VmsTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
}
|
||||
|
||||
VmsTab.displayName = 'VmsTab'
|
||||
|
||||
export default VmsTab
|
@ -0,0 +1,64 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Alert, LinearProgress } from '@mui/material'
|
||||
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetSecGroupQuery } from 'client/features/OneApi/securityGroup'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
import Info from 'client/components/Tabs/SecurityGroup/Info'
|
||||
import Vms from 'client/components/Tabs/SecurityGroup/Vms'
|
||||
|
||||
const getTabComponent = (tabName) =>
|
||||
({
|
||||
info: Info,
|
||||
vms: Vms,
|
||||
}[tabName])
|
||||
|
||||
const SecurityGroupTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isLoading, isError, error } = useGetSecGroupQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
const resource = RESOURCE_NAMES.SEC_GROUP
|
||||
const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {}
|
||||
|
||||
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
|
||||
}, [view])
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Alert severity="error" variant="outlined">
|
||||
{error.data}
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
||||
return isLoading ? (
|
||||
<LinearProgress color="secondary" sx={{ width: '100%' }} />
|
||||
) : (
|
||||
<Tabs addBorder tabs={tabsAvailable ?? []} />
|
||||
)
|
||||
})
|
||||
|
||||
SecurityGroupTabs.propTypes = { id: PropTypes.string.isRequired }
|
||||
SecurityGroupTabs.displayName = 'SecurityGroupTabs'
|
||||
|
||||
export default SecurityGroupTabs
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { T } from 'client/constants'
|
||||
import * as ACTIONS from 'client/constants/actions'
|
||||
|
||||
/**
|
||||
* @typedef SecurityGroupRule
|
||||
@ -116,3 +117,18 @@ export const RULE_TYPE_STRING = {
|
||||
OUTBOUND: T.Outbound,
|
||||
INBOUND: T.Inbound,
|
||||
}
|
||||
|
||||
/** @enum {string} Image actions */
|
||||
export const SEC_GROUP_ACTIONS = {
|
||||
CREATE_DIALOG: 'create_dialog',
|
||||
UPDATE_DIALOG: 'update_dialog',
|
||||
DELETE: 'delete',
|
||||
COMMIT: 'commit',
|
||||
CLONE: 'clone',
|
||||
|
||||
// INFORMATION
|
||||
RENAME: ACTIONS.RENAME,
|
||||
CHANGE_OWNER: ACTIONS.CHANGE_OWNER,
|
||||
CHANGE_GROUP: ACTIONS.CHANGE_GROUP,
|
||||
CHANGE_TYPE: 'chtype',
|
||||
}
|
||||
|
@ -56,9 +56,13 @@ module.exports = {
|
||||
ChangeOwner: 'Change owner',
|
||||
Clear: 'Clear',
|
||||
ClickToCopy: 'Click to copy',
|
||||
Confirm: 'Confirm',
|
||||
Commit: 'Commit',
|
||||
Clone: 'Clone',
|
||||
CloneSeveralTemplates: 'Clone several Templates',
|
||||
CloneTemplate: 'Clone Template',
|
||||
CloneSecGroup: 'Clone Security Group',
|
||||
CloneSecGroups: 'Clone Security Groups',
|
||||
Close: 'Close',
|
||||
Collapse: 'Collapse',
|
||||
Configuration: 'Configuration',
|
||||
@ -72,6 +76,7 @@ module.exports = {
|
||||
CreateVirtualNetwork: 'Create Virtual Network',
|
||||
CreateVmTemplate: 'Create VM Template',
|
||||
CreateImage: 'Create Image',
|
||||
CreateSecurityGroup: 'Create Security Group',
|
||||
CreateFile: 'Create File',
|
||||
CreateDockerfile: 'Create Dockerfile',
|
||||
CurrentGroup: 'Current group: %s',
|
||||
@ -870,6 +875,8 @@ module.exports = {
|
||||
CopyOf: 'Copy of ',
|
||||
PrefixMultipleConcept:
|
||||
'Several templates are selected, please choose prefix to name the new copies',
|
||||
PrefixSecGroupsMultipleConcept:
|
||||
'Several security groups are selected, please choose a prefix to name the new copies Prefix',
|
||||
NewTemplateNameConcept: 'New Image name',
|
||||
CloneWithImages: 'Clone with images',
|
||||
CloneWithImagesConcept: `
|
||||
@ -1017,6 +1024,7 @@ module.exports = {
|
||||
Any: 'Any',
|
||||
Protocol: 'Protocol',
|
||||
IcmpType: 'ICMP Type',
|
||||
IcmpTypeV6: 'ICMPv6 Type',
|
||||
|
||||
/* Host schema */
|
||||
IM_MAD: 'IM MAD',
|
||||
@ -1130,9 +1138,25 @@ module.exports = {
|
||||
DefaultValue: 'Default value',
|
||||
Mandatory: 'Mandatory',
|
||||
PressKeysToAddAValue: 'Press any of the following keys to add a value: %s',
|
||||
|
||||
/** Security Groups */
|
||||
Start: 'Start',
|
||||
Rules: 'Rules',
|
||||
PortRange: 'Port Range',
|
||||
FirstIPIPv6Address: 'First IP/IPv6 address',
|
||||
TargetNetwork: 'Target Network',
|
||||
AnyNetwork: 'Any Network',
|
||||
ManualNetwork: 'Manual Network',
|
||||
OpennebulaVirtualNetwork: 'OpenNebula Virtual Network',
|
||||
SelectNewNetwork: 'Please select a network from the list',
|
||||
NotVmsCurrentySecGroups:
|
||||
'There are currently no VMs associated with this Security Group',
|
||||
CommitMessageSecGroups: `
|
||||
Please note: each time the rules are edited, the commit operation is done automatically.
|
||||
This action will force the propagation of security group changes to VMs.
|
||||
The operation takes time to iterate over all VMs in the security group,
|
||||
the progress can be checked in the "VMs" panel.`,
|
||||
/** Image */
|
||||
NotVmsCurrenty: 'There are currently no VMs associated with this image',
|
||||
NotVmsCurrentyImage: 'There are currently no VMs associated with this image',
|
||||
NotSnapshotCurrenty:
|
||||
'There are currently no snapshots associated with this image',
|
||||
|
||||
|
@ -88,6 +88,9 @@ const InfoTabs = memo(({ file, gotoPage, unselect }) => {
|
||||
return (
|
||||
<Stack overflow="auto">
|
||||
<Stack direction="row" alignItems="center" gap={1} mb={1}>
|
||||
<Typography color="text.primary" noWrap flexGrow={1}>
|
||||
{`#${id} | ${name}`}
|
||||
</Typography>
|
||||
<SubmitButton
|
||||
data-cy="detail-refresh"
|
||||
icon={<RefreshDouble />}
|
||||
@ -111,9 +114,6 @@ const InfoTabs = memo(({ file, gotoPage, unselect }) => {
|
||||
onClick={() => unselect()}
|
||||
/>
|
||||
)}
|
||||
<Typography color="text.primary" noWrap>
|
||||
{`#${id} | ${name}`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<FileTabs id={file.ID} />
|
||||
</Stack>
|
||||
|
@ -87,7 +87,10 @@ const InfoTabs = memo(({ image, gotoPage, unselect }) => {
|
||||
|
||||
return (
|
||||
<Stack overflow="auto">
|
||||
<Stack direction="row" alignItems="center" gap={1} mb={1}>
|
||||
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
|
||||
<Typography color="text.primary" noWrap flexGrow={1}>
|
||||
{`#${id} | ${name}`}
|
||||
</Typography>
|
||||
<SubmitButton
|
||||
data-cy="detail-refresh"
|
||||
icon={<RefreshDouble />}
|
||||
@ -111,9 +114,6 @@ const InfoTabs = memo(({ image, gotoPage, unselect }) => {
|
||||
onClick={() => unselect()}
|
||||
/>
|
||||
)}
|
||||
<Typography color="text.primary" noWrap>
|
||||
{`#${id} | ${name}`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<ImageTabs id={image.ID} />
|
||||
</Stack>
|
||||
|
79
src/fireedge/src/client/containers/SecurityGroups/Create.js
Normal file
79
src/fireedge/src/client/containers/SecurityGroups/Create.js
Normal file
@ -0,0 +1,79 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 { jsonToXml } from 'client/models/Helper'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import {
|
||||
useAllocateSecGroupMutation,
|
||||
useUpdateSecGroupMutation,
|
||||
useGetSecGroupQuery,
|
||||
} from 'client/features/OneApi/securityGroup'
|
||||
|
||||
import {
|
||||
DefaultFormStepper,
|
||||
SkeletonStepsForm,
|
||||
} from 'client/components/FormStepper'
|
||||
import { CreateForm } from 'client/components/Forms/SecurityGroups'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
|
||||
/**
|
||||
* Displays the creation or modification form to a VM Template.
|
||||
*
|
||||
* @returns {ReactElement} VM Template form
|
||||
*/
|
||||
function CreateSecGroup() {
|
||||
const history = useHistory()
|
||||
const { state: { ID: secID, NAME } = {} } = useLocation()
|
||||
|
||||
const [allocate] = useAllocateSecGroupMutation()
|
||||
const [update] = useUpdateSecGroupMutation()
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
|
||||
const { data } = useGetSecGroupQuery({ id: secID })
|
||||
|
||||
const onSubmit = async ({ template }) => {
|
||||
try {
|
||||
if (!secID) {
|
||||
const newTemplateId = await allocate({
|
||||
template: jsonToXml(template),
|
||||
}).unwrap()
|
||||
history.push(PATH.NETWORK.SEC_GROUPS.LIST)
|
||||
enqueueSuccess(`Security Group created - #${newTemplateId}`)
|
||||
} else {
|
||||
await update({ id: secID, template: jsonToXml(template) }).unwrap()
|
||||
history.push(PATH.NETWORK.SEC_GROUPS.LIST)
|
||||
enqueueSuccess(`Security Group updated - #${secID} ${NAME}`)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return secID && !data ? (
|
||||
<SkeletonStepsForm />
|
||||
) : (
|
||||
<CreateForm
|
||||
onSubmit={onSubmit}
|
||||
fallback={<SkeletonStepsForm />}
|
||||
initialValues={data}
|
||||
stepProps={data}
|
||||
>
|
||||
{(config) => <DefaultFormStepper {...config} />}
|
||||
</CreateForm>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateSecGroup
|
157
src/fireedge/src/client/containers/SecurityGroups/index.js
Normal file
157
src/fireedge/src/client/containers/SecurityGroups/index.js
Normal file
@ -0,0 +1,157 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import GotoIcon from 'iconoir-react/dist/Pin'
|
||||
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
|
||||
import Cancel from 'iconoir-react/dist/Cancel'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { useLazyGetSecGroupQuery } from 'client/features/OneApi/securityGroup'
|
||||
import { SecurityGroupsTable } from 'client/components/Tables'
|
||||
import SecurityGroupsActions from 'client/components/Tables/SecurityGroups/actions'
|
||||
import SecurityGroupsTabs from 'client/components/Tabs/SecurityGroup'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T, Image } from 'client/constants'
|
||||
|
||||
/**
|
||||
* Displays a list of Security Groups with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Security Groups list and selected row(s)
|
||||
*/
|
||||
function SecurityGroups() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = SecurityGroupsActions()
|
||||
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
|
||||
return (
|
||||
<SplitPane gridTemplateRows="1fr auto 1fr">
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box height={1} {...(hasSelectedRows && getGridProps())}>
|
||||
<SecurityGroupsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs
|
||||
securityGroup={selectedRows[0]?.original}
|
||||
gotoPage={selectedRows[0]?.gotoPage}
|
||||
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of Security Group.
|
||||
*
|
||||
* @param {Image} securityGroup - Security Group to display
|
||||
* @param {Function} [gotoPage] - Function to navigate to a page of an Security Group
|
||||
* @param {Function} [unselect] - Function to unselect a Security Group
|
||||
* @returns {ReactElement} Security Group details
|
||||
*/
|
||||
const InfoTabs = memo(({ securityGroup, gotoPage, unselect }) => {
|
||||
const [getSecurityGroup, { data: lazyData, isFetching }] =
|
||||
useLazyGetSecGroupQuery()
|
||||
const id = lazyData?.ID ?? securityGroup.ID
|
||||
const name = lazyData?.NAME ?? securityGroup.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>
|
||||
<SubmitButton
|
||||
data-cy="detail-refresh"
|
||||
icon={<RefreshDouble />}
|
||||
tooltip={Tr(T.Refresh)}
|
||||
isSubmitting={isFetching}
|
||||
onClick={() => getSecurityGroup({ 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()}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
<SecurityGroupsTabs id={securityGroup.ID} />
|
||||
</Stack>
|
||||
)
|
||||
})
|
||||
|
||||
InfoTabs.propTypes = {
|
||||
securityGroup: PropTypes.object.isRequired,
|
||||
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 SecurityGroups
|
36
src/fireedge/src/client/containers/VirtualNetworks/Detail.js
Normal file
36
src/fireedge/src/client/containers/VirtualNetworks/Detail.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2022, 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 VNetworkTabs from 'client/components/Tabs/VNetwork'
|
||||
|
||||
/**
|
||||
* Displays the detail information about a VM Template.
|
||||
*
|
||||
* @returns {ReactElement} VM Template detail component.
|
||||
*/
|
||||
function VNetworkDetail() {
|
||||
const { id } = useParams()
|
||||
|
||||
if (Number.isNaN(+id)) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
return <VNetworkTabs id={id} />
|
||||
}
|
||||
|
||||
export default VNetworkDetail
|
@ -19,7 +19,7 @@ import {
|
||||
ONE_RESOURCES,
|
||||
ONE_RESOURCES_POOL,
|
||||
} from 'client/features/OneApi'
|
||||
import { FilterFlag } from 'client/constants'
|
||||
import { FilterFlag, Permission } from 'client/constants'
|
||||
|
||||
const { SECURITYGROUP } = ONE_RESOURCES
|
||||
const { SECURITYGROUP_POOL } = ONE_RESOURCES_POOL
|
||||
@ -75,6 +75,169 @@ const securityGroupApi = oneApi.injectEndpoints({
|
||||
transformResponse: (data) => data?.SECURITY_GROUP ?? {},
|
||||
providesTags: (_, __, { id }) => [{ type: SECURITYGROUP, id }],
|
||||
}),
|
||||
renameSecGroup: builder.mutation({
|
||||
/**
|
||||
* Renames a Security Group.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string} params.id - Security group id
|
||||
* @param {string} params.name - The new name
|
||||
* @returns {number} Security group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_RENAME
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: SECURITYGROUP, id },
|
||||
SECURITYGROUP_POOL,
|
||||
],
|
||||
}),
|
||||
changeSecGroupPermissions: builder.mutation({
|
||||
/**
|
||||
* Changes the permission bits of a Image.
|
||||
* If set any permission to -1, it's not changed.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Image id
|
||||
* @param {Permission|'-1'} params.ownerUse - User use
|
||||
* @param {Permission|'-1'} params.ownerManage - User manage
|
||||
* @param {Permission|'-1'} params.ownerAdmin - User administrator
|
||||
* @param {Permission|'-1'} params.groupUse - Group use
|
||||
* @param {Permission|'-1'} params.groupManage - Group manage
|
||||
* @param {Permission|'-1'} params.groupAdmin - Group administrator
|
||||
* @param {Permission|'-1'} params.otherUse - Other use
|
||||
* @param {Permission|'-1'} params.otherManage - Other manage
|
||||
* @param {Permission|'-1'} params.otherAdmin - Other administrator
|
||||
* @returns {number} Image id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_CHMOD
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: SECURITYGROUP, id }],
|
||||
}),
|
||||
changeSecGroupOwnership: builder.mutation({
|
||||
/**
|
||||
* Changes the ownership of Security Group.
|
||||
* If set `user` or `group` to -1, it's not changed.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Security Group id
|
||||
* @param {string|number|'-1'} [params.userId] - User id
|
||||
* @param {Permission|'-1'} [params.groupId] - Group id
|
||||
* @returns {number} Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_CHOWN
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: SECURITYGROUP, id },
|
||||
SECURITYGROUP_POOL,
|
||||
],
|
||||
}),
|
||||
allocateSecGroup: builder.mutation({
|
||||
/**
|
||||
* Allocates a new Security Group in OpenNebula.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.template - A string containing the template of the Security Group on syntax XML
|
||||
* @returns {number} Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_ALLOCATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [SECURITYGROUP_POOL],
|
||||
}),
|
||||
cloneSegGroup: builder.mutation({
|
||||
/**
|
||||
* Clones an existing Security Group.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.id - The id of the Security Group to be cloned
|
||||
* @param {string} params.name - Name for the new Security Group
|
||||
* @returns {number} The new Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_CLONE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [SECURITYGROUP_POOL],
|
||||
}),
|
||||
removeSecGroup: builder.mutation({
|
||||
/**
|
||||
* Deletes the given Security Group from the pool.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.id - The object id
|
||||
* @returns {number} Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_DELETE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [SECURITYGROUP_POOL],
|
||||
}),
|
||||
updateSecGroup: builder.mutation({
|
||||
/**
|
||||
* Replaces the Security Group template contents.
|
||||
*
|
||||
* @param {number|string} params - Request params
|
||||
* @param {string} params.id - Security Group id
|
||||
* @param {string} params.template - The new template contents on syntax XML
|
||||
* @param {0|1} params.replace -
|
||||
* - Update type:
|
||||
* ``0``: Replace the whole template.
|
||||
* ``1``: Merge new template with the existing one.
|
||||
* @returns {number} Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_UPDATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: SECURITYGROUP, id }],
|
||||
}),
|
||||
commitSegGroup: builder.mutation({
|
||||
/**
|
||||
* Commit an existing Security Group.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.id - The id of the Security Group to be cloned
|
||||
* @param {string} params.vms - Vms for the new Security Group
|
||||
* @returns {number} The new Security Group id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.SECGROUP_COMMIT
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [SECURITYGROUP_POOL],
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@ -84,6 +247,14 @@ export const {
|
||||
useLazyGetSecGroupQuery,
|
||||
useGetSecGroupsQuery,
|
||||
useLazyGetSecGroupsQuery,
|
||||
useRenameSecGroupMutation,
|
||||
useAllocateSecGroupMutation,
|
||||
useCloneSegGroupMutation,
|
||||
useRemoveSecGroupMutation,
|
||||
useUpdateSecGroupMutation,
|
||||
useCommitSegGroupMutation,
|
||||
useChangeSecGroupPermissionsMutation,
|
||||
useChangeSecGroupOwnershipMutation,
|
||||
} = securityGroupApi
|
||||
|
||||
export default securityGroupApi
|
||||
|
Loading…
x
Reference in New Issue
Block a user