1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-02-01 05:47:01 +03:00

F OpenNebula/one#6029: Add NAME and INCREMENT_ID on FSunstone (#2505)

(cherry picked from commit ef6faaa0cdf9a72b65830e6e01441099c8c7daa7)
This commit is contained in:
Frederick Borges 2023-02-15 18:09:46 +01:00 committed by Tino Vázquez
parent f47bfa4a5c
commit 7591cf0dd3
5 changed files with 106 additions and 45 deletions

View File

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and * * See the License for the specific language governing permissions and *
* limitations under the License. * * limitations under the License. *
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
import { boolean, object, ObjectSchema } from 'yup' import { INPUT_TYPES, T } from 'client/constants'
import { Field, getValidationFromFields } from 'client/utils' import { timeFromMilliseconds } from 'client/models/Helper'
import { T, INPUT_TYPES } from 'client/constants' import { Field, arrayToOptions, getValidationFromFields } from 'client/utils'
import { ObjectSchema, boolean, object, string } from 'yup'
const NO_NIC = { const NO_NIC = {
name: 'no_nic', name: 'no_nic',
@ -33,14 +34,41 @@ const NO_IP = {
grid: { xs: 12, md: 6 }, grid: { xs: 12, md: 6 },
} }
/** const NAME = {
* @returns {Field[]} Fields name: 'name',
*/ label: T.Name,
export const FIELDS = () => [NO_NIC, NO_IP] type: INPUT_TYPES.TEXT,
validation: string(),
grid: { xs: 12, md: 6 },
}
const INCREMENT_ID = ({ increments = [] }) => ({
name: 'increment_id',
label: T.IncrementId,
type: INPUT_TYPES.SELECT,
values: arrayToOptions(increments, {
addEmpty: true,
getText: (increment) =>
`${increment.id}: ${timeFromMilliseconds(increment.date)
.toFormat('ff')
.replace(',', '')} (${increment.source})`,
getValue: (increment) => increment.id,
}),
validation: string(),
grid: { xs: 12, md: 6 },
fieldProps: {
disabled: increments.length === 0,
},
})
/** /**
* @param {object} [stepProps] - Step props * @param {object} [data] - Backup data
* @returns {Field[]} Fields
*/
export const FIELDS = (data = {}) => [NAME, INCREMENT_ID(data), NO_NIC, NO_IP]
/**
* @param {object} [data] - Backup data
* @returns {ObjectSchema} Schema * @returns {ObjectSchema} Schema
*/ */
export const SCHEMA = (stepProps) => export const SCHEMA = (data) => object(getValidationFromFields(FIELDS(data)))
object(getValidationFromFields(FIELDS(stepProps)))

View File

@ -21,11 +21,23 @@ import DatastoresTable, {
} from 'client/components/Forms/Backup/RestoreForm/Steps/DatastoresTable' } from 'client/components/Forms/Backup/RestoreForm/Steps/DatastoresTable'
import { createSteps } from 'client/utils' import { createSteps } from 'client/utils'
const Steps = createSteps( const Steps = createSteps([BasicConfiguration, DatastoresTable], {
(app) => [BasicConfiguration, DatastoresTable].filter(Boolean), transformInitialValue: (increments, schema) => {
{ const castedValuesBasic = schema.cast(
transformInitialValue: (app, schema) => { [BASIC_ID]: { increments } },
schema.cast({}, { context: { app } }), { stripUnknown: true }
)
const castedValuesDatastore = schema.cast(
{ [DATASTORE_ID]: {} },
{ stripUnknown: true }
)
return {
[BASIC_ID]: castedValuesBasic[BASIC_ID],
[DATASTORE_ID]: castedValuesDatastore[DATASTORE_ID],
}
},
transformBeforeSubmit: (formData) => { transformBeforeSubmit: (formData) => {
const { [BASIC_ID]: configuration, [DATASTORE_ID]: [datastore] = [] } = const { [BASIC_ID]: configuration, [DATASTORE_ID]: [datastore] = [] } =
formData formData
@ -35,7 +47,6 @@ const Steps = createSteps(
...configuration, ...configuration,
} }
}, },
} })
)
export default Steps export default Steps

View File

@ -24,11 +24,11 @@ import {
useRestoreBackupMutation, useRestoreBackupMutation,
} from 'client/features/OneApi/image' } from 'client/features/OneApi/image'
import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm'
import { RestoreForm } from 'client/components/Forms/Backup' import { RestoreForm } from 'client/components/Forms/Backup'
import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm'
import { import {
createActions,
GlobalAction, GlobalAction,
createActions,
} from 'client/components/Tables/Enhanced/Utils' } from 'client/components/Tables/Enhanced/Utils'
import { Translate } from 'client/components/HOC' import { Translate } from 'client/components/HOC'
@ -67,7 +67,7 @@ const MessageToConfirmAction = (rows) => (
) )
/** /**
* Generates the actions to operate resources on Image table. * Generates the actions to operate resources on Backup table.
* *
* @returns {GlobalAction} - Actions * @returns {GlobalAction} - Actions
*/ */
@ -79,7 +79,7 @@ const Actions = () => {
const resourcesView = getResourceView(RESOURCE_NAMES.BACKUP)?.actions const resourcesView = getResourceView(RESOURCE_NAMES.BACKUP)?.actions
const imageActions = useMemo( const backupActions = useMemo(
() => () =>
createActions({ createActions({
filters: resourcesView, filters: resourcesView,
@ -90,22 +90,42 @@ const Actions = () => {
dataCy: `image-${IMAGE_ACTIONS.RESTORE}`, dataCy: `image-${IMAGE_ACTIONS.RESTORE}`,
label: T.Restore, label: T.Restore,
tooltip: T.Restore, tooltip: T.Restore,
selected: { min: 1 }, selected: { max: 1 },
options: [ options: [
{ {
dialogProps: { dialogProps: {
title: T.SelectCluster, title: T.RestoreBackup,
dataCy: 'modal-select-cluster', dataCy: 'modal-select-cluster',
}, },
form: (rows) => RestoreForm(), form: (row) => {
const backup = row?.[0]?.original
let increments = backup?.BACKUP_INCREMENTS?.INCREMENT || []
increments = Array.isArray(increments)
? increments
: [increments]
increments = increments.map((increment) => ({
id: increment.ID,
date: increment.DATE,
source: increment.SOURCE,
}))
return RestoreForm({
stepProps: { increments },
initialValues: increments,
})
},
onSubmit: (rows) => async (formData) => { onSubmit: (rows) => async (formData) => {
const ids = rows?.map?.(({ original }) => original?.ID) const ids = rows?.map?.(({ original }) => original?.ID)
let options = `NO_IP="${formData.no_ip}"\nNO_NIC="${formData.no_nic}"\n`
if (formData.name) options += `NAME="${formData.name}"\n`
if (formData.increment_id !== '')
options += `INCREMENT_ID="${formData.increment_id}"\n`
await Promise.all( await Promise.all(
ids.map((id) => ids.map((id) =>
restoreBackup({ restoreBackup({
id: id, id: id,
datastore: formData.datastore, datastore: formData.datastore,
options: `NO_IP="${formData.no_ip}"\nNO_NIC="${formData.no_nic}"`, options,
}) })
) )
) )
@ -183,7 +203,7 @@ const Actions = () => {
[view] [view]
) )
return imageActions return backupActions
} }
export default Actions export default Actions

View File

@ -413,6 +413,8 @@ module.exports = {
Incremental: 'Incremental', Incremental: 'Incremental',
Mode: 'Mode', Mode: 'Mode',
ResetBackup: 'Reset', ResetBackup: 'Reset',
IncrementId: 'Increment ID',
RestoreBackup: 'Restore backup',
/* sections - templates & instances */ /* sections - templates & instances */
Instances: 'Instances', Instances: 'Instances',

View File

@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and * * See the License for the specific language governing permissions and *
* limitations under the License. * * limitations under the License. *
* ------------------------------------------------------------------------- */ * ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react' import { Box, Chip, Stack, Typography } from '@mui/material'
import PropTypes from 'prop-types' import Cancel from 'iconoir-react/dist/Cancel'
import GotoIcon from 'iconoir-react/dist/Pin' import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble' import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel' import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material' import { ReactElement, memo, useState } from 'react'
import { Row } from 'react-table' import { Row } from 'react-table'
import { useLazyGetImageQuery } from 'client/features/OneApi/image' import { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import MultipleTags from 'client/components/MultipleTags'
import SplitPane from 'client/components/SplitPane'
import { BackupsTable } from 'client/components/Tables' import { BackupsTable } from 'client/components/Tables'
import BackupActions from 'client/components/Tables/Backups/actions' import BackupActions from 'client/components/Tables/Backups/actions'
import BackupTabs from 'client/components/Tabs/Backup' import BackupTabs from 'client/components/Tabs/Backup'
import SplitPane from 'client/components/SplitPane' import { Image, T } from 'client/constants'
import MultipleTags from 'client/components/MultipleTags' import { useLazyGetImageQuery } from 'client/features/OneApi/image'
import { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Image } from 'client/constants'
/** /**
* Displays a list of Backups with a split pane between the list and selected row(s). * Displays a list of Backups with a split pane between the list and selected row(s).
@ -38,7 +38,7 @@ import { T, Image } from 'client/constants'
*/ */
function Backups() { function Backups() {
const [selectedRows, onSelectedRowsChange] = useState(() => []) const [selectedRows, onSelectedRowsChange] = useState(() => [])
const actions = BackupActions() const actions = BackupActions(selectedRows)
const hasSelectedRows = selectedRows?.length > 0 const hasSelectedRows = selectedRows?.length > 0
const moreThanOneSelected = selectedRows?.length > 1 const moreThanOneSelected = selectedRows?.length > 1