mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-22 13:33:52 +03:00
parent
72a5dee464
commit
5f0d555d07
@ -2982,7 +2982,8 @@ FIREEDGE_SUNSTONE_ETC_VIEW_ADMIN="src/fireedge/etc/sunstone/admin/vm-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/backup-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/datastore-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/vdc-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/user-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/user-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/backupjobs-tab.yaml \
|
||||
src/fireedge/etc/sunstone/admin/host-tab.yaml"
|
||||
|
||||
FIREEDGE_SUNSTONE_ETC_VIEW_USER="src/fireedge/etc/sunstone/user/vm-tab.yaml \
|
||||
|
69
src/fireedge/etc/sunstone/admin/backupjobs-tab.yaml
Normal file
69
src/fireedge/etc/sunstone/admin/backupjobs-tab.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
# This file describes the information and actions available in the Backup Jobs tab
|
||||
|
||||
# Resource
|
||||
|
||||
resource_name: "BACKUPJOBS"
|
||||
|
||||
# Actions - Which buttons are visible to operate over the resources
|
||||
|
||||
actions:
|
||||
create_dialog: true
|
||||
update_dialog: true
|
||||
delete: true
|
||||
chown: true
|
||||
chgrp: true
|
||||
lock: true
|
||||
unlock: true
|
||||
start: true
|
||||
cancel: true
|
||||
|
||||
# Filters - List of criteria to filter the resources
|
||||
|
||||
filters:
|
||||
label: true
|
||||
|
||||
# Info Tabs - Which info tabs are used to show extended information
|
||||
|
||||
info-tabs:
|
||||
info:
|
||||
enabled: true
|
||||
information_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
rename: true
|
||||
priority: true
|
||||
permissions_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
chmod: true
|
||||
ownership_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
chown: true
|
||||
chgrp: true
|
||||
attributes_panel:
|
||||
enabled: true
|
||||
actions:
|
||||
copy: true
|
||||
add: true
|
||||
edit: true
|
||||
delete: true
|
||||
|
||||
vms:
|
||||
enabled: true
|
||||
sched_actions:
|
||||
enabled: true
|
||||
actions:
|
||||
sched_action_create: true
|
||||
sched_action_update: true
|
||||
sched_action_delete: true
|
||||
|
||||
|
||||
# Dialogs
|
||||
dialogs:
|
||||
create_dialog:
|
||||
general: true
|
||||
vms: true
|
||||
datastores: true
|
||||
sched_actions: true
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import {
|
||||
RefreshDouble as BackupIcon,
|
||||
ClockOutline as BackupJobsIcon,
|
||||
Server as ClusterIcon,
|
||||
Db as DatastoreIcon,
|
||||
Archive as FileIcon,
|
||||
@ -233,6 +234,24 @@ const VDCCreate = loadable(() => import('client/containers/VDCs/Create'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
const BackupJobs = loadable(() => import('client/containers/BackupJobs'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
const BackupJobsDetail = loadable(
|
||||
() => import('client/containers/BackupJobs/Detail'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
|
||||
const BackupJobsCreate = loadable(
|
||||
() => import('client/containers/BackupJobs/Create'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
)
|
||||
|
||||
// const ACLs = loadable(() => import('client/containers/ACLs'), { ssr: false })
|
||||
|
||||
export const PATH = {
|
||||
@ -299,6 +318,11 @@ export const PATH = {
|
||||
DETAIL: `/${RESOURCE_NAMES.APP}/:id`,
|
||||
CREATE: `/${RESOURCE_NAMES.APP}/create`,
|
||||
},
|
||||
BACKUPJOBS: {
|
||||
LIST: `/${RESOURCE_NAMES.BACKUPJOBS}`,
|
||||
DETAIL: `/${RESOURCE_NAMES.BACKUPJOBS}/:id`,
|
||||
CREATE: `/${RESOURCE_NAMES.BACKUPJOBS}/create`,
|
||||
},
|
||||
},
|
||||
NETWORK: {
|
||||
VNETS: {
|
||||
@ -558,6 +582,24 @@ const ENDPOINTS = [
|
||||
path: PATH.STORAGE.MARKETPLACE_APPS.CREATE,
|
||||
Component: CreateMarketplaceApp,
|
||||
},
|
||||
{
|
||||
title: T.CreateBackupJob,
|
||||
path: PATH.STORAGE.BACKUPJOBS.CREATE,
|
||||
Component: BackupJobsCreate,
|
||||
},
|
||||
{
|
||||
title: T.BackupJobs,
|
||||
path: PATH.STORAGE.BACKUPJOBS.LIST,
|
||||
sidebar: true,
|
||||
icon: BackupJobsIcon,
|
||||
Component: BackupJobs,
|
||||
},
|
||||
{
|
||||
title: T.BackupJob,
|
||||
description: (params) => `#${params?.id}`,
|
||||
path: PATH.STORAGE.BACKUPJOBS.DETAIL,
|
||||
Component: BackupJobsDetail,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -19,10 +19,11 @@ import { ReactElement, memo, useMemo } from 'react'
|
||||
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import {
|
||||
CreateBackupJobSchedActionForm,
|
||||
CreateCharterForm,
|
||||
CreateRelativeCharterForm,
|
||||
CreateSchedActionForm,
|
||||
CreateRelativeSchedActionForm,
|
||||
CreateSchedActionForm,
|
||||
} from 'client/components/Forms/Vm'
|
||||
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
@ -33,7 +34,7 @@ import {
|
||||
VM_ACTIONS,
|
||||
VM_ACTIONS_IN_CHARTER,
|
||||
} from 'client/constants'
|
||||
import { sentenceCase, hasRestrictedAttributes } from 'client/utils'
|
||||
import { hasRestrictedAttributes, sentenceCase } from 'client/utils'
|
||||
|
||||
/**
|
||||
* Returns a button to trigger form to create a scheduled action.
|
||||
@ -45,34 +46,38 @@ import { sentenceCase, hasRestrictedAttributes } from 'client/utils'
|
||||
* @returns {ReactElement} Button
|
||||
*/
|
||||
const CreateSchedButton = memo(
|
||||
({ vm, relative, onSubmit, oneConfig, adminGroup }) => (
|
||||
<ButtonToTriggerForm
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': VM_ACTIONS.SCHED_ACTION_CREATE,
|
||||
label: T.AddAction,
|
||||
variant: 'outlined',
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
name: T.PunctualAction,
|
||||
dialogProps: {
|
||||
title: T.ScheduleAction,
|
||||
dataCy: 'modal-sched-actions',
|
||||
({ vm, relative, onSubmit, oneConfig, adminGroup, backupjobs }) => {
|
||||
const formConfig = {
|
||||
stepProps: { vm, oneConfig, adminGroup },
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonToTriggerForm
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': VM_ACTIONS.SCHED_ACTION_CREATE,
|
||||
label: T.AddAction,
|
||||
variant: 'outlined',
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
name: T.PunctualAction,
|
||||
dialogProps: {
|
||||
title: T.ScheduleAction,
|
||||
dataCy: 'modal-sched-actions',
|
||||
},
|
||||
form: () =>
|
||||
relative
|
||||
? CreateRelativeSchedActionForm(formConfig)
|
||||
: backupjobs
|
||||
? CreateBackupJobSchedActionForm(formConfig)
|
||||
: CreateSchedActionForm(formConfig),
|
||||
onSubmit,
|
||||
},
|
||||
form: () =>
|
||||
relative
|
||||
? CreateRelativeSchedActionForm({
|
||||
stepProps: { vm, oneConfig, adminGroup },
|
||||
})
|
||||
: CreateSchedActionForm({
|
||||
stepProps: { vm, oneConfig, adminGroup },
|
||||
}),
|
||||
onSubmit,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
@ -86,7 +91,7 @@ const CreateSchedButton = memo(
|
||||
* @returns {ReactElement} Button
|
||||
*/
|
||||
const UpdateSchedButton = memo(
|
||||
({ vm, schedule, relative, onSubmit, oneConfig, adminGroup }) => {
|
||||
({ vm, schedule, relative, onSubmit, oneConfig, adminGroup, backupjobs }) => {
|
||||
const { ID, ACTION } = schedule
|
||||
const titleAction = `#${ID} ${sentenceCase(ACTION)}`
|
||||
const formConfig = {
|
||||
@ -115,6 +120,8 @@ const UpdateSchedButton = memo(
|
||||
form: () =>
|
||||
relative
|
||||
? CreateRelativeSchedActionForm(formConfig)
|
||||
: backupjobs
|
||||
? CreateBackupJobSchedActionForm(formConfig)
|
||||
: CreateSchedActionForm(formConfig),
|
||||
onSubmit,
|
||||
},
|
||||
@ -226,6 +233,7 @@ const ButtonPropTypes = {
|
||||
schedule: PropTypes.object,
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
backupjobs: PropTypes.bool,
|
||||
}
|
||||
|
||||
CreateSchedButton.propTypes = ButtonPropTypes
|
||||
|
156
src/fireedge/src/client/components/Cards/BackupJobCard.js
Normal file
156
src/fireedge/src/client/components/Cards/BackupJobCard.js
Normal file
@ -0,0 +1,156 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement, memo, useMemo } from 'react'
|
||||
|
||||
import { Typography } from '@mui/material'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { StatusCircle } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
import Timer from 'client/components/Timer'
|
||||
import { T } from 'client/constants'
|
||||
import { Group, HighPriority, Lock, User } from 'iconoir-react'
|
||||
|
||||
import COLOR from 'client/constants/color'
|
||||
import { timeFromMilliseconds } from 'client/models/Helper'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const haveValues = function (object) {
|
||||
return Object.values(object).length > 0
|
||||
}
|
||||
|
||||
const BackupJobCard = memo(
|
||||
/**
|
||||
* @param {object} props - Props
|
||||
* @param {object} props.template - BackupJob template
|
||||
* @param {object} props.rootProps - Props to root component
|
||||
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
|
||||
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
|
||||
* @returns {ReactElement} - Card
|
||||
*/
|
||||
({ template, rootProps, onClickLabel, onDeleteLabel }) => {
|
||||
const classes = rowStyles()
|
||||
|
||||
const {
|
||||
ID,
|
||||
NAME,
|
||||
UNAME,
|
||||
GNAME,
|
||||
OUTDATED_VMS,
|
||||
BACKING_UP_VMS,
|
||||
ERROR_VMS,
|
||||
PRIORITY,
|
||||
LAST_BACKUP_TIME,
|
||||
LOCK,
|
||||
} = template
|
||||
|
||||
const time = useMemo(() => {
|
||||
const LastBackupTime = +LAST_BACKUP_TIME
|
||||
if (LastBackupTime > 0) {
|
||||
const timer = timeFromMilliseconds(LastBackupTime)
|
||||
|
||||
return (
|
||||
<span title={timer.toFormat('ff')}>
|
||||
<Timer translateWord={T.LastBackupTime} initial={timer} />
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}, [LAST_BACKUP_TIME])
|
||||
|
||||
const status = useMemo(() => {
|
||||
const completed = {
|
||||
color: COLOR.success.main,
|
||||
tooltip: T.Completed,
|
||||
}
|
||||
const noStarted = {
|
||||
color: COLOR.warning.main,
|
||||
tooltip: T.NotStartedYet,
|
||||
}
|
||||
|
||||
const error = {
|
||||
color: COLOR.error.main,
|
||||
tooltip: T.Error,
|
||||
}
|
||||
|
||||
const onGoing = {
|
||||
color: COLOR.info.main,
|
||||
tooltip: T.OnGoing,
|
||||
}
|
||||
|
||||
if (haveValues(ERROR_VMS)) {
|
||||
return error
|
||||
}
|
||||
|
||||
if (!haveValues(OUTDATED_VMS) && !haveValues(BACKING_UP_VMS)) {
|
||||
return LAST_BACKUP_TIME === '0' ? noStarted : completed
|
||||
}
|
||||
|
||||
if (haveValues(OUTDATED_VMS)) {
|
||||
return completed
|
||||
}
|
||||
|
||||
if (haveValues(BACKING_UP_VMS)) {
|
||||
return onGoing
|
||||
}
|
||||
}, [OUTDATED_VMS, BACKING_UP_VMS, ERROR_VMS, LAST_BACKUP_TIME])
|
||||
|
||||
return (
|
||||
<div {...rootProps} data-cy={`backupjob-${ID}`}>
|
||||
<div className={classes.main}>
|
||||
<div className={classes.title}>
|
||||
<StatusCircle color={status.color} tooltip={Tr(status.tooltip)} />
|
||||
<Typography component="span">{NAME}</Typography>
|
||||
<span className={classes.labels}>
|
||||
{LOCK && <Lock data-cy="lock" />}
|
||||
</span>
|
||||
</div>
|
||||
<div className={classes.caption}>
|
||||
<span>{`#${ID}`}</span>
|
||||
<span title={`${Tr(T.Priority)}: ${PRIORITY}`}>
|
||||
<HighPriority />
|
||||
<span>{` ${PRIORITY}`}</span>
|
||||
</span>
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
{time}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
BackupJobCard.propTypes = {
|
||||
template: PropTypes.object,
|
||||
rootProps: PropTypes.shape({
|
||||
className: PropTypes.string,
|
||||
}),
|
||||
onClickLabel: PropTypes.func,
|
||||
onDeleteLabel: PropTypes.func,
|
||||
}
|
||||
|
||||
BackupJobCard.displayName = 'BackupJobCard'
|
||||
|
||||
export default BackupJobCard
|
@ -19,9 +19,11 @@ import { ReactElement, memo, useMemo } from 'react'
|
||||
import { Box, Stack, Tooltip, Typography } from '@mui/material'
|
||||
import {
|
||||
Cpu,
|
||||
Group,
|
||||
HardDrive,
|
||||
Lock,
|
||||
Network,
|
||||
User,
|
||||
WarningCircledOutline as WarningIcon,
|
||||
} from 'iconoir-react'
|
||||
|
||||
@ -75,6 +77,8 @@ const VirtualMachineCard = memo(
|
||||
ETIME,
|
||||
LOCK,
|
||||
USER_TEMPLATE: { LABELS } = {},
|
||||
GNAME,
|
||||
UNAME,
|
||||
TEMPLATE: { VCPU = '-', MEMORY } = {},
|
||||
} = vm
|
||||
|
||||
@ -181,6 +185,18 @@ const VirtualMachineCard = memo(
|
||||
<HardDrive />
|
||||
<span data-cy="hostname">{HOSTNAME}</span>
|
||||
</span>
|
||||
{!!UNAME && (
|
||||
<span title={`${Tr(T.Owner)}: ${UNAME}`}>
|
||||
<User />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
)}
|
||||
{!!GNAME && (
|
||||
<span title={`${Tr(T.Group)}: ${GNAME}`}>
|
||||
<Group />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
)}
|
||||
{!!ips?.length && (
|
||||
<span title={`${Tr(T.IP)}`}>
|
||||
<Network />
|
||||
|
@ -17,6 +17,7 @@ import AddressRangeCard from 'client/components/Cards/AddressRangeCard'
|
||||
import ApplicationCard from 'client/components/Cards/ApplicationCard'
|
||||
import ApplicationNetworkCard from 'client/components/Cards/ApplicationNetworkCard'
|
||||
import ApplicationTemplateCard from 'client/components/Cards/ApplicationTemplateCard'
|
||||
import BackupJobCard from 'client/components/Cards/BackupJobCard'
|
||||
import ClusterCard from 'client/components/Cards/ClusterCard'
|
||||
import DatastoreCard from 'client/components/Cards/DatastoreCard'
|
||||
import DiskCard from 'client/components/Cards/DiskCard'
|
||||
@ -51,6 +52,7 @@ export {
|
||||
ApplicationCard,
|
||||
ApplicationNetworkCard,
|
||||
ApplicationTemplateCard,
|
||||
BackupJobCard,
|
||||
ClusterCard,
|
||||
DatastoreCard,
|
||||
DiskCard,
|
||||
|
@ -0,0 +1,36 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
import {
|
||||
FIELDS,
|
||||
SCHEMA,
|
||||
} from 'client/components/Forms/BackupJob/AddVmsForm/schema'
|
||||
import { createForm } from 'client/utils'
|
||||
|
||||
const AddVmsForm = createForm(SCHEMA, FIELDS, {
|
||||
transformInitialValue: ({ BACKUP_VMS: backupVms }, schema) => {
|
||||
const vms = (backupVms && backupVms?.split(',')) || []
|
||||
|
||||
return {
|
||||
...schema.cast(
|
||||
{ BACKUP_VMS: vms.join(','), VMS: vms },
|
||||
{ stripUnknown: true }
|
||||
),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default AddVmsForm
|
@ -0,0 +1,84 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ObjectSchema, array, object, string } from 'yup'
|
||||
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
import { INPUT_TYPES, T } from 'client/constants'
|
||||
import { Field, getValidationFromFields } from 'client/utils'
|
||||
|
||||
const VMS_NAME = 'VMS'
|
||||
const BACKUP_VMS_NAME = 'BACKUP_VMS'
|
||||
|
||||
/** @type {Field} DataTable field */
|
||||
const VMS = {
|
||||
name: VMS_NAME,
|
||||
type: INPUT_TYPES.TABLE,
|
||||
Table: () => VmsTable,
|
||||
singleSelect: false,
|
||||
validation: array(string().trim())
|
||||
.required()
|
||||
.default(() => undefined),
|
||||
grid: { md: 12 },
|
||||
value: (values, form) => {
|
||||
const { VMS: vms } = values || {}
|
||||
if (vms && form?.setValue) {
|
||||
form?.setValue(VMS_NAME, vms)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/** @type {Field} Order Backup Vms field */
|
||||
export const BACKUP_VMS = {
|
||||
name: BACKUP_VMS_NAME,
|
||||
label: T.VMsBackupJobOrder,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
dependOf: [VMS_NAME],
|
||||
watcher:
|
||||
([vms = []] = []) =>
|
||||
(value = '') => {
|
||||
const arrayValue = (value && value?.split(',')) || []
|
||||
let rtn = []
|
||||
|
||||
rtn = arrayValue
|
||||
vms.forEach((vm) => {
|
||||
if (!rtn.includes(vm)) {
|
||||
rtn.push(vm)
|
||||
}
|
||||
})
|
||||
const positionDelete = []
|
||||
rtn.forEach((vm, i) => {
|
||||
if (!vms.includes(vm)) {
|
||||
positionDelete.push(i)
|
||||
}
|
||||
})
|
||||
positionDelete
|
||||
.sort((a, b) => b - a)
|
||||
.forEach((index) => {
|
||||
rtn.splice(index, 1)
|
||||
})
|
||||
|
||||
return rtn.join(',')
|
||||
},
|
||||
multiline: true,
|
||||
validation: string().trim(),
|
||||
grid: { md: 12 },
|
||||
}
|
||||
|
||||
/** @type {Field[]} List of fields */
|
||||
export const FIELDS = [VMS, BACKUP_VMS]
|
||||
|
||||
/** @type {ObjectSchema} Schema */
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,70 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import { SCHEMA } from 'client/components/Forms/BackupJob/CreateForm/Steps/DatastoreTable/schema'
|
||||
import { DatastoresTable } from 'client/components/Tables'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { Step } from 'client/utils'
|
||||
|
||||
export const STEP_ID = 'datastores'
|
||||
|
||||
const Content = ({ data }) => {
|
||||
const { NAME } = data?.[0] ?? {}
|
||||
const { setValue } = useFormContext()
|
||||
|
||||
const handleSelectedRows = (rows) => {
|
||||
const dataRows = rows?.map?.(({ original }) => original)
|
||||
setValue(STEP_ID, dataRows)
|
||||
}
|
||||
|
||||
return (
|
||||
<DatastoresTable
|
||||
singleSelect
|
||||
disableGlobalSort
|
||||
displaySelectedRows
|
||||
pageSize={5}
|
||||
getRowId={(row) => String(row.NAME)}
|
||||
initialState={{
|
||||
selectedRowIds: { [NAME]: true },
|
||||
}}
|
||||
onSelectedRowsChange={handleSelectedRows}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to select the Datastore.
|
||||
*
|
||||
* @param {object} app - Marketplace App resource
|
||||
* @returns {Step} Datastore step
|
||||
*/
|
||||
const DatastoresStep = (app) => ({
|
||||
id: STEP_ID,
|
||||
label: T.SelectDatastores,
|
||||
resolver: SCHEMA,
|
||||
content: (props) => Content({ ...props, app }),
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
data: PropTypes.any,
|
||||
setFormData: PropTypes.func,
|
||||
app: PropTypes.object,
|
||||
}
|
||||
|
||||
export default DatastoresStep
|
@ -0,0 +1,21 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ArraySchema, array, object } from 'yup'
|
||||
|
||||
/** @type {ArraySchema} Datastore table schema */
|
||||
export const SCHEMA = array(object())
|
||||
.ensure()
|
||||
.default(() => [])
|
@ -0,0 +1,41 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
|
||||
import { SCHEMA, FIELDS } from './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 VM Template.
|
||||
*
|
||||
* @returns {object} General configuration step
|
||||
*/
|
||||
const General = () => ({
|
||||
id: STEP_ID,
|
||||
label: T.General,
|
||||
resolver: SCHEMA,
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: Content,
|
||||
})
|
||||
|
||||
export default General
|
@ -0,0 +1,106 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import {
|
||||
BACKUP_MODE_OPTIONS,
|
||||
FS_FREEZE_OPTIONS,
|
||||
INPUT_TYPES,
|
||||
T,
|
||||
} from 'client/constants'
|
||||
|
||||
import { Field, arrayToOptions, getValidationFromFields } from 'client/utils'
|
||||
import { ObjectSchema, boolean, number, object, string } from 'yup'
|
||||
|
||||
const NAME = {
|
||||
name: 'NAME',
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string().trim().required(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
const PRIORITY = {
|
||||
name: 'PRIORITY',
|
||||
label: T.Priority,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: 'number',
|
||||
validation: number()
|
||||
.positive()
|
||||
.required()
|
||||
.default(() => 1),
|
||||
}
|
||||
|
||||
const FS_FREEZE = {
|
||||
name: 'FS_FREEZE',
|
||||
label: T.FSFreeze,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(Object.keys(FS_FREEZE_OPTIONS), {
|
||||
getText: (type) => type,
|
||||
getValue: (type) => FS_FREEZE_OPTIONS[type],
|
||||
}),
|
||||
validation: string().trim(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
const MODE = {
|
||||
name: 'MODE',
|
||||
label: T.Mode,
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: arrayToOptions(Object.keys(BACKUP_MODE_OPTIONS), {
|
||||
addEmpty: true,
|
||||
getText: (type) => type,
|
||||
getValue: (type) => BACKUP_MODE_OPTIONS[type],
|
||||
}),
|
||||
validation: string().trim(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
const KEEP_LAST = {
|
||||
name: 'KEEP_LAST',
|
||||
label: T.KeepLast,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
htmlType: 'number',
|
||||
validation: number()
|
||||
.positive()
|
||||
.required()
|
||||
.default(() => 1),
|
||||
}
|
||||
|
||||
/** @type {Field} Persistent field */
|
||||
export const BACKUP_VOLATILE = {
|
||||
name: 'BACKUP_VOLATILE',
|
||||
label: T.MakePersistent,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { xs: 12, md: 6 },
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Field[]} Fields
|
||||
*/
|
||||
export const FIELDS = [
|
||||
NAME,
|
||||
PRIORITY,
|
||||
FS_FREEZE,
|
||||
MODE,
|
||||
KEEP_LAST,
|
||||
BACKUP_VOLATILE,
|
||||
]
|
||||
|
||||
/**
|
||||
* @param {object} [stepProps] - Step props
|
||||
* @returns {ObjectSchema} Schema
|
||||
*/
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS))
|
@ -0,0 +1,143 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Box, Stack, styled } from '@mui/material'
|
||||
import {
|
||||
CreateSchedButton,
|
||||
DeleteSchedButton,
|
||||
UpdateSchedButton,
|
||||
} from 'client/components/Buttons/ScheduleAction'
|
||||
import { ScheduleActionCard } from 'client/components/Cards'
|
||||
import { T } from 'client/constants'
|
||||
import { Step, cleanEmpty } from 'client/utils'
|
||||
import { useCallback } from 'react'
|
||||
import { useFormContext, useWatch } from 'react-hook-form'
|
||||
import { array } from 'yup'
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export const STEP_ID = 'SCHED_ACTION'
|
||||
|
||||
const StyledContainer = styled(Box)(({ theme }) => ({
|
||||
marginTop: `${theme.spacing(0.5)}`,
|
||||
}))
|
||||
|
||||
const Content = ({ oneConfig, adminGroup }) => {
|
||||
const { setValue } = useFormContext()
|
||||
const scheduleActions = useWatch({
|
||||
name: STEP_ID,
|
||||
defaultValue: [],
|
||||
})
|
||||
|
||||
const handleAction = useCallback(
|
||||
(type, action, index) => {
|
||||
const newScheduleActions = [...(scheduleActions ?? [])]
|
||||
const updatedScheduleAction = {
|
||||
...action,
|
||||
ACTION: 'backup',
|
||||
}
|
||||
switch (type) {
|
||||
case 'create':
|
||||
newScheduleActions.push(updatedScheduleAction)
|
||||
break
|
||||
case 'update':
|
||||
newScheduleActions[index] = updatedScheduleAction
|
||||
break
|
||||
default:
|
||||
newScheduleActions.splice(index, 1)
|
||||
break
|
||||
}
|
||||
setValue(STEP_ID, cleanEmpty(newScheduleActions))
|
||||
},
|
||||
[scheduleActions]
|
||||
)
|
||||
|
||||
const actions = scheduleActions ?? []
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<Stack flexDirection="row" gap="1em">
|
||||
<CreateSchedButton
|
||||
onSubmit={(newAction) => handleAction('create', newAction)}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
backupjobs
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
pb="1em"
|
||||
display="grid"
|
||||
gridTemplateColumns="repeat(auto-fit, minmax(300px, 0.5fr))"
|
||||
gap="1em"
|
||||
mt="1em"
|
||||
>
|
||||
{actions?.map((schedule, index) => {
|
||||
const { ID, NAME } = schedule
|
||||
const fakeValues = { ...schedule, ID: index }
|
||||
|
||||
return (
|
||||
<ScheduleActionCard
|
||||
key={ID ?? NAME}
|
||||
schedule={fakeValues}
|
||||
actions={
|
||||
<>
|
||||
<UpdateSchedButton
|
||||
relative
|
||||
vm={{}}
|
||||
schedule={fakeValues}
|
||||
onSubmit={(newAction) =>
|
||||
handleAction('update', newAction, index)
|
||||
}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
<DeleteSchedButton
|
||||
schedule={fakeValues}
|
||||
onSubmit={() => handleAction('delete', index)}
|
||||
oneConfig={oneConfig}
|
||||
adminGroup={adminGroup}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Stack>
|
||||
</StyledContainer>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to select the Schedule Actions.
|
||||
*
|
||||
* @param {object} app - app resource
|
||||
* @returns {Step} Schedule Action step
|
||||
*/
|
||||
const ScheduleActions = (app) => ({
|
||||
id: STEP_ID,
|
||||
label: T.ScheduleAction,
|
||||
resolver: array().ensure(),
|
||||
content: (props) => Content({ ...props, app }),
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
data: PropTypes.any,
|
||||
setFormData: PropTypes.func,
|
||||
oneConfig: PropTypes.object,
|
||||
adminGroup: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default ScheduleActions
|
@ -0,0 +1,69 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
|
||||
import { SCHEMA } from 'client/components/Forms/BackupJob/CreateForm/Steps/VmsTable/schema'
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { Step } from 'client/utils'
|
||||
|
||||
export const STEP_ID = 'vms'
|
||||
|
||||
const Content = ({ data }) => {
|
||||
const { NAME } = data?.[0] ?? {}
|
||||
const { setValue } = useFormContext()
|
||||
|
||||
const handleSelectedRows = (rows) => {
|
||||
const dataRows = rows?.map?.(({ original }) => original)
|
||||
setValue(STEP_ID, dataRows)
|
||||
}
|
||||
|
||||
return (
|
||||
<VmsTable
|
||||
disableGlobalSort
|
||||
displaySelectedRows
|
||||
pageSize={5}
|
||||
getRowId={(row) => String(row.NAME)}
|
||||
initialState={{
|
||||
selectedRowIds: { [NAME]: true },
|
||||
}}
|
||||
onSelectedRowsChange={handleSelectedRows}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to select the Vms.
|
||||
*
|
||||
* @param {object} app - BackupJob App resource
|
||||
* @returns {Step} Vms step
|
||||
*/
|
||||
const VmsStep = (app) => ({
|
||||
id: STEP_ID,
|
||||
label: T.SelectVms,
|
||||
resolver: SCHEMA,
|
||||
content: (props) => Content({ ...props, app }),
|
||||
})
|
||||
|
||||
Content.propTypes = {
|
||||
data: PropTypes.any,
|
||||
setFormData: PropTypes.func,
|
||||
app: PropTypes.object,
|
||||
}
|
||||
|
||||
export default VmsStep
|
@ -0,0 +1,21 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ArraySchema, array, object } from 'yup'
|
||||
|
||||
/** @type {ArraySchema} Datastore table schema */
|
||||
export const SCHEMA = array(object())
|
||||
.ensure()
|
||||
.default(() => [])
|
@ -0,0 +1,46 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import Datastore, { STEP_ID as DATASTORE_ID } from './DatastoreTable'
|
||||
import General, { STEP_ID as GENERAL_ID } from './General'
|
||||
import ScheduleActions, { STEP_ID as SCHEDULE_ID } from './ScheduleActions'
|
||||
import Vms, { STEP_ID as VMS_ID } from './VmsTable'
|
||||
|
||||
import { createSteps, extractIDValues } from 'client/utils'
|
||||
|
||||
const Steps = createSteps([General, Vms, Datastore, ScheduleActions], {
|
||||
transformBeforeSubmit: (formData) => {
|
||||
const {
|
||||
[GENERAL_ID]: general = {},
|
||||
[VMS_ID]: vms = [],
|
||||
[DATASTORE_ID]: datastores = [],
|
||||
[SCHEDULE_ID]: scheduleactions = [],
|
||||
} = formData ?? {}
|
||||
|
||||
const jsonTemplate = {
|
||||
...general,
|
||||
BACKUP_VMS: extractIDValues(vms),
|
||||
DATASTORE_ID: extractIDValues(datastores),
|
||||
}
|
||||
|
||||
if (scheduleactions.length) {
|
||||
jsonTemplate.SCHED_ACTION = scheduleactions
|
||||
}
|
||||
|
||||
return jsonTemplate
|
||||
},
|
||||
})
|
||||
|
||||
export default Steps
|
@ -0,0 +1,16 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
export { default } from 'client/components/Forms/BackupJob/CreateForm/Steps'
|
34
src/fireedge/src/client/components/Forms/BackupJob/index.js
Normal file
34
src/fireedge/src/client/components/Forms/BackupJob/index.js
Normal file
@ -0,0 +1,34 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { AsyncLoadForm, ConfigurationProps } from 'client/components/HOC'
|
||||
import { CreateFormCallback, CreateStepsCallback } from 'client/utils/schema'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
*/
|
||||
const AddVmsForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'BackupJob/AddVmsForm' }, configProps)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateStepsCallback} Asynchronous loaded form
|
||||
*/
|
||||
const CreateForm = (configProps) =>
|
||||
AsyncLoadForm({ formPath: 'BackupJob/CreateForm' }, configProps)
|
||||
|
||||
export { AddVmsForm, CreateForm }
|
@ -14,22 +14,22 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Grow, Menu, MenuItem, Typography, ListItemIcon } from '@mui/material'
|
||||
import { Grow, ListItemIcon, Menu, MenuItem, Typography } from '@mui/material'
|
||||
import { NavArrowDown } from 'iconoir-react'
|
||||
|
||||
import { useDialog } from 'client/hooks'
|
||||
import {
|
||||
DialogConfirmation,
|
||||
DialogForm,
|
||||
DialogPropTypes,
|
||||
} from 'client/components/Dialogs'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import FormStepper from 'client/components/FormStepper'
|
||||
import FormWithSchema from 'client/components/Forms/FormWithSchema'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { useDialog } from 'client/hooks'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
|
||||
const ButtonToTriggerForm = ({
|
||||
|
@ -24,6 +24,8 @@ import { DateTime } from 'luxon'
|
||||
import { createForm } from 'client/utils'
|
||||
|
||||
import {
|
||||
BACKUPJOB_SCHED_FIELDS,
|
||||
BACKUPJOB_SCHED_SCHEMA,
|
||||
TEMPLATE_SCHED_FIELDS,
|
||||
TEMPLATE_SCHED_SCHEMA,
|
||||
VM_SCHED_FIELDS,
|
||||
@ -127,6 +129,15 @@ const RelativeForm = createForm(TEMPLATE_SCHED_SCHEMA, TEMPLATE_SCHED_FIELDS, {
|
||||
transformBeforeSubmit: commonTransformBeforeSubmit,
|
||||
})
|
||||
|
||||
export { RelativeForm }
|
||||
const BackupJobForm = createForm(
|
||||
BACKUPJOB_SCHED_SCHEMA,
|
||||
BACKUPJOB_SCHED_FIELDS,
|
||||
{
|
||||
transformInitialValue: commonTransformInitialValue,
|
||||
transformBeforeSubmit: commonTransformBeforeSubmit,
|
||||
}
|
||||
)
|
||||
|
||||
export { BackupJobForm, RelativeForm }
|
||||
|
||||
export default CreateSchedActionForm
|
||||
|
@ -23,7 +23,7 @@ import {
|
||||
} from 'client/components/Forms/Vm/CreateSchedActionForm/fields'
|
||||
import { ARGS_TYPES } from 'client/constants'
|
||||
import { getRequiredArgsByAction } from 'client/models/Scheduler'
|
||||
import { Field, getObjectSchemaFromFields, disableFields } from 'client/utils'
|
||||
import { Field, disableFields, getObjectSchemaFromFields } from 'client/utils'
|
||||
|
||||
const ARG_SCHEMA = string()
|
||||
.trim()
|
||||
@ -136,3 +136,29 @@ export const TEMPLATE_SCHED_SCHEMA = COMMON_SCHEMA.concat(
|
||||
PUNCTUAL_FIELDS.END_VALUE_FIELD,
|
||||
])
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {object} props - Props
|
||||
* @param {object} props.vm - Vm resource
|
||||
* @returns {Field[]} Fields
|
||||
*/
|
||||
export const BACKUPJOB_SCHED_FIELDS = ({ vm }) => [
|
||||
...COMMON_FIELDS(vm, true),
|
||||
PUNCTUAL_FIELDS.TIME_FIELD,
|
||||
PUNCTUAL_FIELDS.END_TYPE_FIELD,
|
||||
PUNCTUAL_FIELDS.END_VALUE_FIELD,
|
||||
]
|
||||
|
||||
/** @type {ObjectSchema} Schema */
|
||||
export const BACKUPJOB_SCHED_SCHEMA = getObjectSchemaFromFields([
|
||||
PUNCTUAL_FIELDS.TIME_FIELD,
|
||||
PUNCTUAL_FIELDS.PERIODIC_FIELD(true),
|
||||
PUNCTUAL_FIELDS.REPEAT_FIELD,
|
||||
PUNCTUAL_FIELDS.WEEKLY_FIELD,
|
||||
PUNCTUAL_FIELDS.MONTHLY_FIELD,
|
||||
PUNCTUAL_FIELDS.YEARLY_FIELD,
|
||||
PUNCTUAL_FIELDS.HOURLY_FIELD,
|
||||
PUNCTUAL_FIELDS.DAYS_FIELD,
|
||||
PUNCTUAL_FIELDS.END_TYPE_FIELD,
|
||||
PUNCTUAL_FIELDS.END_VALUE_FIELD,
|
||||
])
|
||||
|
@ -13,9 +13,9 @@
|
||||
* 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, CreateStepsCallback } from 'client/utils/schema'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
@ -139,6 +139,16 @@ const CreateRelativeSchedActionForm = (configProps) =>
|
||||
configProps
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
*/
|
||||
const CreateBackupJobSchedActionForm = (configProps) =>
|
||||
AsyncLoadForm(
|
||||
{ formPath: 'Vm/CreateSchedActionForm', componentToLoad: 'BackupJobForm' },
|
||||
configProps
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {ConfigurationProps} configProps - Configuration
|
||||
* @returns {ReactElement|CreateFormCallback} Asynchronous loaded form
|
||||
@ -176,6 +186,7 @@ export {
|
||||
BackupForm,
|
||||
ChangeGroupForm,
|
||||
ChangeUserForm,
|
||||
CreateBackupJobSchedActionForm,
|
||||
CreateCharterForm,
|
||||
CreateDiskSnapshotForm,
|
||||
CreateRelativeCharterForm,
|
||||
|
252
src/fireedge/src/client/components/Tables/BackupJobs/actions.js
Normal file
252
src/fireedge/src/client/components/Tables/BackupJobs/actions.js
Normal file
@ -0,0 +1,252 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Typography } from '@mui/material'
|
||||
import { useMemo } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import {
|
||||
useCancelBackupJobMutation,
|
||||
useChangeBackupJobOwnershipMutation,
|
||||
useLockBackupJobMutation,
|
||||
useRemoveBackupJobMutation,
|
||||
useStartBackupJobMutation,
|
||||
useUnlockBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
import { AddCircledOutline, Group, Lock, Trash } from 'iconoir-react'
|
||||
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import {
|
||||
createActions,
|
||||
GlobalAction,
|
||||
} from 'client/components/Tables/Enhanced/Utils'
|
||||
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { BACKUPJOB_ACTIONS, RESOURCE_NAMES, T } from 'client/constants'
|
||||
import { isAvailableAction } from 'client/models/VirtualMachine'
|
||||
|
||||
const isDisabled = (action) => (rows) =>
|
||||
!isAvailableAction(
|
||||
action,
|
||||
rows.map(({ original }) => original)
|
||||
)
|
||||
|
||||
const ListBackupJobsNames = ({ rows = [] }) =>
|
||||
rows?.map?.(({ id, original }) => {
|
||||
const { ID, NAME } = original
|
||||
|
||||
return (
|
||||
<Typography
|
||||
key={`backupjob-${id}`}
|
||||
variant="inherit"
|
||||
component="span"
|
||||
display="block"
|
||||
>
|
||||
{`#${ID} ${NAME}`}
|
||||
</Typography>
|
||||
)
|
||||
})
|
||||
|
||||
const SubHeader = (rows) => <ListBackupJobsNames rows={rows} />
|
||||
|
||||
const MessageToConfirmAction = (rows, description) => (
|
||||
<>
|
||||
<ListBackupJobsNames rows={rows} />
|
||||
{description && <Translate word={description} />}
|
||||
<Translate word={T.DoYouWantProceed} />
|
||||
</>
|
||||
)
|
||||
|
||||
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
|
||||
|
||||
/**
|
||||
* Generates the actions to operate resources on Backup Jobs Template table.
|
||||
*
|
||||
* @returns {GlobalAction} - Actions
|
||||
*/
|
||||
const Actions = () => {
|
||||
const history = useHistory()
|
||||
const { view, getResourceView } = useViews()
|
||||
const [changeOwnership] = useChangeBackupJobOwnershipMutation()
|
||||
const [deleteBackupJob] = useRemoveBackupJobMutation()
|
||||
const [lock] = useLockBackupJobMutation()
|
||||
const [unlock] = useUnlockBackupJobMutation()
|
||||
const [start] = useStartBackupJobMutation()
|
||||
const [cancel] = useCancelBackupJobMutation()
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
createActions({
|
||||
filters: getResourceView(RESOURCE_NAMES.BACKUPJOBS)?.actions,
|
||||
actions: [
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.CREATE_DIALOG,
|
||||
dataCy: `backupjob_${BACKUPJOB_ACTIONS.CREATE_DIALOG}`,
|
||||
tooltip: T.Create,
|
||||
icon: AddCircledOutline,
|
||||
action: () => history.push(PATH.STORAGE.BACKUPJOBS.CREATE),
|
||||
},
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.START,
|
||||
label: T.Start,
|
||||
tooltip: T.Start,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Lock,
|
||||
dataCy: `modal-${BACKUPJOB_ACTIONS.START}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => start({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.CANCEL,
|
||||
label: T.Cancel,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Cancel,
|
||||
dataCy: `modal-${BACKUPJOB_ACTIONS.CANCEL}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => cancel({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tooltip: T.Ownership,
|
||||
icon: Group,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
dataCy: 'backupjob-ownership',
|
||||
options: [
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.CHANGE_OWNER,
|
||||
disabled: isDisabled(BACKUPJOB_ACTIONS.CHANGE_OWNER),
|
||||
name: T.ChangeOwner,
|
||||
dialogProps: {
|
||||
title: T.ChangeOwner,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${BACKUPJOB_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: BACKUPJOB_ACTIONS.CHANGE_GROUP,
|
||||
disabled: isDisabled(BACKUPJOB_ACTIONS.CHANGE_GROUP),
|
||||
name: T.ChangeGroup,
|
||||
dialogProps: {
|
||||
title: T.ChangeGroup,
|
||||
subheader: SubHeader,
|
||||
dataCy: `modal-${BACKUPJOB_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 }))
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tooltip: T.Lock,
|
||||
icon: Lock,
|
||||
selected: true,
|
||||
color: 'secondary',
|
||||
dataCy: 'backupjob-lock',
|
||||
options: [
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.LOCK,
|
||||
name: T.Lock,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Lock,
|
||||
dataCy: `modal-${BACKUPJOB_ACTIONS.LOCK}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => lock({ id })))
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.UNLOCK,
|
||||
name: T.Unlock,
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Unlock,
|
||||
dataCy: `modal-${BACKUPJOB_ACTIONS.UNLOCK}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => unlock({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
accessor: BACKUPJOB_ACTIONS.DELETE,
|
||||
tooltip: T.Delete,
|
||||
icon: Trash,
|
||||
color: 'error',
|
||||
selected: { min: 1 },
|
||||
dataCy: `backupjob_${BACKUPJOB_ACTIONS.DELETE}`,
|
||||
options: [
|
||||
{
|
||||
isConfirmDialog: true,
|
||||
dialogProps: {
|
||||
title: T.Delete,
|
||||
dataCy: `modal-${BACKUPJOB_ACTIONS.DELETE}`,
|
||||
children: MessageToConfirmAction,
|
||||
},
|
||||
onSubmit: (rows) => async () => {
|
||||
const ids = rows?.map?.(({ original }) => original?.ID)
|
||||
await Promise.all(ids.map((id) => deleteBackupJob({ id })))
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[view]
|
||||
)
|
||||
}
|
||||
|
||||
export default Actions
|
@ -0,0 +1,28 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Column } from 'react-table'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
|
||||
/** @type {Column[]} VM Template columns */
|
||||
const COLUMNS = [
|
||||
{ Header: T.ID, id: 'id', accessor: 'ID', sortType: 'number' },
|
||||
{ Header: T.Name, id: 'name', accessor: 'NAME' },
|
||||
]
|
||||
|
||||
COLUMNS.noFilterIds = ['id', 'name']
|
||||
|
||||
export default COLUMNS
|
@ -0,0 +1,67 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetBackupJobsQuery } from 'client/features/OneApi/backupjobs'
|
||||
|
||||
import BackupJobsColumns from 'client/components/Tables/BackupJobs/columns'
|
||||
import BackupJobsRow from 'client/components/Tables/BackupJobs/row'
|
||||
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
|
||||
const DEFAULT_DATA_CY = 'backupjobs'
|
||||
|
||||
/**
|
||||
* @param {object} props - Props
|
||||
* @returns {ReactElement} Backup Jobs table
|
||||
*/
|
||||
const BackupJobsTable = (props) => {
|
||||
const { rootProps = {}, searchProps = {}, ...rest } = props ?? {}
|
||||
rootProps['data-cy'] ??= DEFAULT_DATA_CY
|
||||
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
|
||||
|
||||
const { view, getResourceView } = useViews()
|
||||
const { data = [], isFetching, refetch } = useGetBackupJobsQuery()
|
||||
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
createColumns({
|
||||
filters: getResourceView(RESOURCE_NAMES.BACKUPJOBS)?.filters,
|
||||
columns: BackupJobsColumns,
|
||||
}),
|
||||
[view]
|
||||
)
|
||||
|
||||
return (
|
||||
<EnhancedTable
|
||||
columns={columns}
|
||||
data={useMemo(() => data, [data])}
|
||||
rootProps={rootProps}
|
||||
searchProps={searchProps}
|
||||
refetch={refetch}
|
||||
isLoading={isFetching}
|
||||
getRowId={(row) => String(row.ID)}
|
||||
RowComponent={BackupJobsRow}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
BackupJobsTable.propTypes = { ...EnhancedTable.propTypes }
|
||||
BackupJobsTable.displayName = 'BackupJobsTable'
|
||||
|
||||
export default BackupJobsTable
|
74
src/fireedge/src/client/components/Tables/BackupJobs/row.js
Normal file
74
src/fireedge/src/client/components/Tables/BackupJobs/row.js
Normal file
@ -0,0 +1,74 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import { memo, useCallback, useMemo } from 'react'
|
||||
|
||||
import { BackupJobCard } from 'client/components/Cards'
|
||||
import backupjobApi, {
|
||||
useUpdateBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
import { jsonToXml } from 'client/models/Helper'
|
||||
|
||||
const Row = memo(
|
||||
({ original, value, onClickLabel, ...props }) => {
|
||||
const [update] = useUpdateBackupJobMutation()
|
||||
|
||||
const state = backupjobApi.endpoints.getBackupJobs.useQueryState(
|
||||
undefined,
|
||||
{
|
||||
selectFromResult: ({ data = [] }) =>
|
||||
data.find((backupjob) => +backupjob.ID === +original.ID),
|
||||
}
|
||||
)
|
||||
|
||||
const memoBackupJob = useMemo(() => state ?? original, [state, original])
|
||||
|
||||
const handleDeleteLabel = useCallback(
|
||||
(label) => {
|
||||
const currentLabels = memoBackupJob.TEMPLATE?.LABELS?.split(',')
|
||||
const newLabels = currentLabels.filter((l) => l !== label).join(',')
|
||||
const newUserTemplate = { ...memoBackupJob.TEMPLATE, LABELS: newLabels }
|
||||
const templateXml = jsonToXml(newUserTemplate)
|
||||
|
||||
update({ id: original.ID, template: templateXml, replace: 0 })
|
||||
},
|
||||
[memoBackupJob.TEMPLATE?.LABELS, update]
|
||||
)
|
||||
|
||||
return (
|
||||
<BackupJobCard
|
||||
template={memoBackupJob}
|
||||
rootProps={props}
|
||||
onClickLabel={onClickLabel}
|
||||
onDeleteLabel={handleDeleteLabel}
|
||||
/>
|
||||
)
|
||||
},
|
||||
(prev, next) => prev.className === next.className
|
||||
)
|
||||
|
||||
Row.propTypes = {
|
||||
original: PropTypes.object,
|
||||
value: PropTypes.object,
|
||||
isSelected: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
onClickLabel: PropTypes.func,
|
||||
}
|
||||
|
||||
Row.displayName = 'VirtualDataCenterRow'
|
||||
|
||||
export default Row
|
@ -35,6 +35,8 @@ const VmsTable = (props) => {
|
||||
searchProps = {},
|
||||
initialState = {},
|
||||
host,
|
||||
backupjobs,
|
||||
backupjobsState,
|
||||
...rest
|
||||
} = props ?? {}
|
||||
|
||||
@ -53,6 +55,7 @@ const VmsTable = (props) => {
|
||||
data:
|
||||
result?.data
|
||||
?.filter((vm) => {
|
||||
// this filters data for host
|
||||
if (host?.ID) {
|
||||
if (
|
||||
host?.ERROR_VMS?.ID ||
|
||||
@ -71,6 +74,24 @@ const VmsTable = (props) => {
|
||||
return [host?.VMS?.ID ?? []].flat().includes(vm.ID)
|
||||
}
|
||||
|
||||
// this filters data for backupjobs
|
||||
if (backupjobs?.ID) {
|
||||
if (backupjobsState) {
|
||||
return [backupjobs?.[backupjobsState]?.ID ?? []]
|
||||
.flat()
|
||||
.includes(vm.ID)
|
||||
} else {
|
||||
return [
|
||||
(backupjobs?.TEMPLATE?.BACKUP_VMS &&
|
||||
backupjobs?.TEMPLATE?.BACKUP_VMS.split(',')) ??
|
||||
[],
|
||||
]
|
||||
.flat()
|
||||
.includes(vm.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// This is for return data without filters
|
||||
return true
|
||||
})
|
||||
?.filter(({ STATE }) => VM_STATES[STATE]?.name !== STATES.DONE) ?? [],
|
||||
|
@ -14,47 +14,47 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import AllImagesTable from 'client/components/Tables/AllImages'
|
||||
import BackupJobsTable from 'client/components/Tables/BackupJobs'
|
||||
import BackupsTable from 'client/components/Tables/Backups'
|
||||
import ClustersTable from 'client/components/Tables/Clusters'
|
||||
import DatastoresTable from 'client/components/Tables/Datastores'
|
||||
import DataGridTable from 'client/components/Tables/DataGrid'
|
||||
import DatastoresTable from 'client/components/Tables/Datastores'
|
||||
import DockerHubTagsTable from 'client/components/Tables/DockerHubTags'
|
||||
import EnhancedTable from 'client/components/Tables/Enhanced'
|
||||
import FilesTable from 'client/components/Tables/Files'
|
||||
import GroupsTable from 'client/components/Tables/Groups'
|
||||
import HostsTable from 'client/components/Tables/Hosts'
|
||||
import ImagesTable from 'client/components/Tables/Images'
|
||||
import IncrementsTable from 'client/components/Tables/Increments'
|
||||
import FilesTable from 'client/components/Tables/Files'
|
||||
import MarketplaceAppsTable from 'client/components/Tables/MarketplaceApps'
|
||||
import MarketplacesTable from 'client/components/Tables/Marketplaces'
|
||||
import SecurityGroupsTable from 'client/components/Tables/SecurityGroups'
|
||||
import ServicesTable from 'client/components/Tables/Services'
|
||||
import ServiceTemplatesTable from 'client/components/Tables/ServiceTemplates'
|
||||
import ServicesTable from 'client/components/Tables/Services'
|
||||
import SkeletonTable from 'client/components/Tables/Skeleton'
|
||||
import UsersTable from 'client/components/Tables/Users'
|
||||
import VNetworkTemplatesTable from 'client/components/Tables/VNetworkTemplates'
|
||||
import VNetworksTable from 'client/components/Tables/VNetworks'
|
||||
import VRoutersTable from 'client/components/Tables/VRouters'
|
||||
import VDCsTable from 'client/components/Tables/VirtualDataCenters'
|
||||
import VirtualizedTable from 'client/components/Tables/Virtualized'
|
||||
import VmsTable from 'client/components/Tables/Vms'
|
||||
import VmGroupsTable from 'client/components/Tables/VmGroups'
|
||||
import VmTemplatesTable from 'client/components/Tables/VmTemplates'
|
||||
import VNetworksTable from 'client/components/Tables/VNetworks'
|
||||
import VNetworkTemplatesTable from 'client/components/Tables/VNetworkTemplates'
|
||||
import VRoutersTable from 'client/components/Tables/VRouters'
|
||||
import VmsTable from 'client/components/Tables/Vms'
|
||||
import ZonesTable from 'client/components/Tables/Zones'
|
||||
import VDCsTable from 'client/components/Tables/VirtualDataCenters'
|
||||
|
||||
export * from 'client/components/Tables/Enhanced/Utils'
|
||||
|
||||
export {
|
||||
AllImagesTable,
|
||||
SkeletonTable,
|
||||
EnhancedTable,
|
||||
BackupJobsTable,
|
||||
BackupsTable,
|
||||
FilesTable,
|
||||
VirtualizedTable,
|
||||
ClustersTable,
|
||||
DataGridTable,
|
||||
DatastoresTable,
|
||||
DockerHubTagsTable,
|
||||
DataGridTable,
|
||||
EnhancedTable,
|
||||
FilesTable,
|
||||
GroupsTable,
|
||||
HostsTable,
|
||||
ImagesTable,
|
||||
@ -62,15 +62,17 @@ export {
|
||||
MarketplaceAppsTable,
|
||||
MarketplacesTable,
|
||||
SecurityGroupsTable,
|
||||
ServicesTable,
|
||||
ServiceTemplatesTable,
|
||||
ServicesTable,
|
||||
SkeletonTable,
|
||||
UsersTable,
|
||||
VDCsTable,
|
||||
VmsTable,
|
||||
VNetworkTemplatesTable,
|
||||
VNetworksTable,
|
||||
VRoutersTable,
|
||||
VirtualizedTable,
|
||||
VmGroupsTable,
|
||||
VmTemplatesTable,
|
||||
VNetworksTable,
|
||||
VNetworkTemplatesTable,
|
||||
VRoutersTable,
|
||||
VmsTable,
|
||||
ZonesTable,
|
||||
}
|
||||
|
146
src/fireedge/src/client/components/Tabs/BackupJobs/Info/index.js
Normal file
146
src/fireedge/src/client/components/Tabs/BackupJobs/Info/index.js
Normal file
@ -0,0 +1,146 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Stack } from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import Information from 'client/components/Tabs/BackupJobs/Info/information'
|
||||
import {
|
||||
AttributePanel,
|
||||
Ownership,
|
||||
Permissions,
|
||||
} from 'client/components/Tabs/Common'
|
||||
import { T } from 'client/constants'
|
||||
import {
|
||||
useChangeBackupJobOwnershipMutation,
|
||||
useChangeBackupJobPermissionsMutation,
|
||||
useGetBackupJobQuery,
|
||||
useUpdateBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
import {
|
||||
filterAttributes,
|
||||
getActionsAvailable,
|
||||
jsonToXml,
|
||||
} from 'client/models/Helper'
|
||||
import { cloneObject, set } from 'client/utils'
|
||||
|
||||
const HIDDEN_BACKUPJOBS_REG = /^(SCHED_ACTION|ERROR)$/
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {object} props.tabProps - Tab information
|
||||
* @param {string} props.id - Template id
|
||||
* @returns {ReactElement} Information tab
|
||||
*/
|
||||
const BackupJobInfoTab = ({ tabProps = {}, id }) => {
|
||||
const {
|
||||
information_panel: informationPanel,
|
||||
permissions_panel: permissionsPanel,
|
||||
ownership_panel: ownershipPanel,
|
||||
attributes_panel: attributesPanel,
|
||||
} = tabProps
|
||||
|
||||
const { data: backupjob = {} } = useGetBackupJobQuery({ id })
|
||||
const { UNAME, UID, GNAME, GID, PERMISSIONS, TEMPLATE } = backupjob
|
||||
const [changeOwnership] = useChangeBackupJobOwnershipMutation()
|
||||
const [changePermissions] = useChangeBackupJobPermissionsMutation()
|
||||
const [update] = useUpdateBackupJobMutation()
|
||||
|
||||
const getActions = (actions) => getActionsAvailable(actions)
|
||||
|
||||
const { attributes } = filterAttributes(TEMPLATE, {
|
||||
hidden: HIDDEN_BACKUPJOBS_REG,
|
||||
})
|
||||
|
||||
const handleChangePermission = async (newPermission) => {
|
||||
await changePermissions({ id, ...newPermission })
|
||||
}
|
||||
|
||||
const handleChangeOwnership = async (newOwnership) => {
|
||||
await changeOwnership({ id, ...newOwnership })
|
||||
}
|
||||
|
||||
const handleAttributeInXml = async (path, newValue) => {
|
||||
const newTemplate = cloneObject(TEMPLATE)
|
||||
set(newTemplate, path, newValue)
|
||||
|
||||
const xml = jsonToXml(newTemplate)
|
||||
await update({ id, template: xml, replace: 0 })
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack
|
||||
display="grid"
|
||||
gap="1em"
|
||||
gridTemplateColumns="repeat(auto-fit, minmax(49%, 1fr))"
|
||||
padding={{ sm: '0.8em' }}
|
||||
>
|
||||
{informationPanel?.enabled && (
|
||||
<Information
|
||||
actions={getActions(informationPanel?.actions)}
|
||||
backupjob={backupjob}
|
||||
/>
|
||||
)}
|
||||
{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}
|
||||
/>
|
||||
)}
|
||||
{attributesPanel?.enabled && (
|
||||
<AttributePanel
|
||||
attributes={attributes}
|
||||
actions={getActions(attributesPanel?.actions)}
|
||||
title={Tr(T.Attributes)}
|
||||
handleAdd={handleAttributeInXml}
|
||||
handleEdit={handleAttributeInXml}
|
||||
handleDelete={handleAttributeInXml}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
BackupJobInfoTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
}
|
||||
|
||||
BackupJobInfoTab.displayName = 'BackupJobInfoTab'
|
||||
|
||||
export default BackupJobInfoTab
|
@ -0,0 +1,106 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
|
||||
import { List } from 'client/components/Tabs/Common'
|
||||
import {
|
||||
useRenameBackupJobMutation,
|
||||
useUpdatePriorityBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
|
||||
import { BACKUPJOB_ACTIONS, T } from 'client/constants'
|
||||
|
||||
import { timeFromMilliseconds } from 'client/models/Helper'
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {object} props.backupjob - Template
|
||||
* @param {string[]} props.actions - Available actions to information tab
|
||||
* @returns {ReactElement} Information tab
|
||||
*/
|
||||
const InformationPanel = ({ backupjob = {}, actions }) => {
|
||||
const [rename] = useRenameBackupJobMutation()
|
||||
const [setPriority] = useUpdatePriorityBackupJobMutation()
|
||||
|
||||
const { ID, NAME, PRIORITY, LAST_BACKUP_TIME, LAST_BACKUP_DURATION } =
|
||||
backupjob
|
||||
|
||||
const time = useMemo(() => {
|
||||
const LastBackupTime = +LAST_BACKUP_TIME
|
||||
if (LastBackupTime > 0) {
|
||||
const timer = timeFromMilliseconds(+LAST_BACKUP_TIME)
|
||||
|
||||
return timer.toFormat('ff')
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}, [LAST_BACKUP_TIME])
|
||||
|
||||
const handleRename = async (_, newName) => {
|
||||
await rename({ id: ID, name: newName })
|
||||
}
|
||||
const handlePriority = async (_, priority) => {
|
||||
await setPriority({ id: ID, priority })
|
||||
}
|
||||
|
||||
const info = [
|
||||
{ name: T.ID, value: ID, dataCy: 'id' },
|
||||
{
|
||||
name: T.Name,
|
||||
value: NAME,
|
||||
canEdit: actions?.includes?.(BACKUPJOB_ACTIONS.RENAME),
|
||||
handleEdit: handleRename,
|
||||
dataCy: 'name',
|
||||
},
|
||||
{
|
||||
name: T.Priority,
|
||||
value: PRIORITY,
|
||||
canEdit: actions?.includes?.(BACKUPJOB_ACTIONS.PRIORITY),
|
||||
handleEdit: handlePriority,
|
||||
dataCy: 'priority',
|
||||
},
|
||||
{
|
||||
name: T.LastBackupTimeInfo,
|
||||
value: time,
|
||||
dataCy: 'lastBackupTime',
|
||||
},
|
||||
{
|
||||
name: T.LastBackupDuration,
|
||||
value: LAST_BACKUP_DURATION,
|
||||
dataCy: 'lastDurationTime',
|
||||
},
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
<List
|
||||
title={T.Information}
|
||||
list={info}
|
||||
containerProps={{ sx: { gridRow: 'span 3' } }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
InformationPanel.displayName = 'InformationPanel'
|
||||
|
||||
InformationPanel.propTypes = {
|
||||
actions: PropTypes.arrayOf(PropTypes.string),
|
||||
backupjob: PropTypes.object,
|
||||
}
|
||||
|
||||
export default InformationPanel
|
@ -0,0 +1,153 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Stack } from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement, useMemo } from 'react'
|
||||
|
||||
import {
|
||||
CreateSchedButton,
|
||||
DeleteSchedButton,
|
||||
UpdateSchedButton,
|
||||
} from 'client/components/Buttons/ScheduleAction'
|
||||
import ScheduleActionCard from 'client/components/Cards/ScheduleActionCard'
|
||||
import {
|
||||
useAddScheduledActionBackupJobMutation,
|
||||
useDeleteScheduledActionBackupJobMutation,
|
||||
useGetBackupJobQuery,
|
||||
useUpdateScheduledActionBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
|
||||
import { VM_ACTIONS } from 'client/constants'
|
||||
import { getActionsAvailable, jsonToXml } from 'client/models/Helper'
|
||||
import { getScheduleActions } from 'client/models/VirtualMachine'
|
||||
|
||||
const { SCHED_ACTION_CREATE, SCHED_ACTION_UPDATE, SCHED_ACTION_DELETE } =
|
||||
VM_ACTIONS
|
||||
|
||||
/**
|
||||
* Renders the list of schedule actions from a VM.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {object|boolean} props.tabProps - Tab properties
|
||||
* @param {object} [props.tabProps.actions] - Actions from user view yaml
|
||||
* @param {string} props.id - Virtual Machine id
|
||||
* @returns {ReactElement} Schedule actions tab
|
||||
*/
|
||||
const BackupJobSchedulingTab = ({ tabProps: { actions } = {}, id }) => {
|
||||
const [addScheduledAction] = useAddScheduledActionBackupJobMutation()
|
||||
const [updateScheduledAction] = useUpdateScheduledActionBackupJobMutation()
|
||||
const [deleteScheduledAction] = useDeleteScheduledActionBackupJobMutation()
|
||||
const { data: backupjob = {} } = useGetBackupJobQuery({ id })
|
||||
|
||||
const [scheduling, actionsAvailable] = useMemo(
|
||||
() => [getScheduleActions(backupjob), getActionsAvailable(actions)],
|
||||
[backupjob]
|
||||
)
|
||||
|
||||
const isCreateEnabled = actionsAvailable?.includes?.(SCHED_ACTION_CREATE)
|
||||
const isUpdateEnabled = actionsAvailable?.includes?.(SCHED_ACTION_UPDATE)
|
||||
const isDeleteEnabled = actionsAvailable?.includes?.(SCHED_ACTION_DELETE)
|
||||
|
||||
/**
|
||||
* Add new schedule action to Backup Job.
|
||||
*
|
||||
* @param {object} formData - New schedule action
|
||||
* @returns {Promise} - Add schedule action and refetch Backup Job data
|
||||
*/
|
||||
const handleCreateSchedAction = async (formData) => {
|
||||
const template = jsonToXml({
|
||||
SCHED_ACTION: { ...formData, ACTION: 'backup' },
|
||||
})
|
||||
await addScheduledAction({ id, template })
|
||||
}
|
||||
|
||||
/**
|
||||
* Update schedule action to Backup Job.
|
||||
*
|
||||
* @param {object} formData - Updated schedule action
|
||||
* @param {string|number} schedId - Schedule action id
|
||||
* @returns {Promise} - Update schedule action and refetch BackupJob data
|
||||
*/
|
||||
const handleUpdate = async (formData, schedId) => {
|
||||
const template = jsonToXml({
|
||||
SCHED_ACTION: { ...formData, ACTION: 'backup' },
|
||||
})
|
||||
await updateScheduledAction({ id, schedId, template })
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete schedule action to BackupJob.
|
||||
*
|
||||
* @param {string|number} schedId - Schedule action id
|
||||
* @returns {Promise} - Delete schedule action and refetch BackupJob data
|
||||
*/
|
||||
const handleRemove = async (schedId) => {
|
||||
await deleteScheduledAction({ id, schedId })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreateEnabled && (
|
||||
<Stack flexDirection="row" gap="1em">
|
||||
<CreateSchedButton
|
||||
vm={backupjob}
|
||||
onSubmit={handleCreateSchedAction}
|
||||
backupjobs
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Stack gap="1em" py="0.8em">
|
||||
{scheduling.map((schedule) => {
|
||||
const { ID, NAME } = schedule
|
||||
|
||||
return (
|
||||
<ScheduleActionCard
|
||||
key={ID ?? NAME}
|
||||
schedule={schedule}
|
||||
actions={({ noMore }) => (
|
||||
<>
|
||||
{isUpdateEnabled && (
|
||||
<UpdateSchedButton
|
||||
vm={backupjob}
|
||||
schedule={schedule}
|
||||
backupjobs
|
||||
onSubmit={(newAction) => handleUpdate(newAction, ID)}
|
||||
/>
|
||||
)}
|
||||
{isDeleteEnabled && (
|
||||
<DeleteSchedButton
|
||||
onSubmit={() => handleRemove(ID)}
|
||||
schedule={schedule}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Stack>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
BackupJobSchedulingTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
}
|
||||
BackupJobSchedulingTab.displayName = 'BackupJobSchedulingTab'
|
||||
|
||||
export default BackupJobSchedulingTab
|
@ -0,0 +1,67 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { AddVmsForm } from 'client/components/Forms/BackupJob'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
import { useUpdateBackupJobMutation } from 'client/features/OneApi/backupjobs'
|
||||
import { jsonToXml } from 'client/models/Helper'
|
||||
import Edit from 'iconoir-react/dist/Edit'
|
||||
import PropTypes from 'prop-types'
|
||||
import { memo } from 'react'
|
||||
|
||||
const AttachVms = memo(({ id, template }) => {
|
||||
const [update] = useUpdateBackupJobMutation()
|
||||
const formConfig = {
|
||||
initialValues: template,
|
||||
}
|
||||
|
||||
const handleEditVms = async ({ BACKUP_VMS } = {}) => {
|
||||
const xml = jsonToXml({ ...template, BACKUP_VMS })
|
||||
await update({ id, template: xml, replace: 0 })
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonToTriggerForm
|
||||
buttonProps={{
|
||||
'data-cy': `edit-vms`,
|
||||
icon: <Edit />,
|
||||
tooltip: Tr(T.Edit),
|
||||
variant: 'outlined',
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
cy: 'edit-vms',
|
||||
name: T.Image,
|
||||
dialogProps: {
|
||||
title: T.SelectVms,
|
||||
dataCy: 'modal-edit-vms',
|
||||
},
|
||||
form: () => AddVmsForm(formConfig),
|
||||
onSubmit: handleEditVms,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
AttachVms.propTypes = {
|
||||
id: PropTypes.string,
|
||||
template: PropTypes.object,
|
||||
}
|
||||
AttachVms.displayName = 'AttachVms'
|
||||
|
||||
export default AttachVms
|
280
src/fireedge/src/client/components/Tabs/BackupJobs/VMs/index.js
Normal file
280
src/fireedge/src/client/components/Tabs/BackupJobs/VMs/index.js
Normal file
@ -0,0 +1,280 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import {
|
||||
Alert,
|
||||
Fade,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
List,
|
||||
ListItem,
|
||||
Paper,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
|
||||
import { ReactElement, useState } from 'react'
|
||||
import { generatePath, useHistory } from 'react-router-dom'
|
||||
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
import AttachVms from 'client/components/Tabs/BackupJobs/VMs/Actions'
|
||||
import { T } from 'client/constants'
|
||||
import {
|
||||
useGetBackupJobQuery,
|
||||
useLazyGetBackupJobQuery,
|
||||
useRetryBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
const useStyles = makeStyles(({ palette, typography }) => ({
|
||||
graphStyle: {
|
||||
'&': {
|
||||
width: '100% !important',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
fontWeight: typography.fontWeightBold,
|
||||
borderBottom: `1px solid ${palette.divider}`,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
stretch: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'stretch',
|
||||
},
|
||||
center: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
},
|
||||
box: {
|
||||
marginBottom: '15px',
|
||||
},
|
||||
alert: {
|
||||
'&': {
|
||||
margin: '15px 0',
|
||||
backgroundColor: palette.background.paper,
|
||||
},
|
||||
},
|
||||
submit: {
|
||||
fontSize: '1em',
|
||||
},
|
||||
checked: {
|
||||
'& svg': {
|
||||
color: 'rgba(168, 168, 168, 0.8)',
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const statBackingUp = 'backinUp'
|
||||
const stateError = 'error'
|
||||
const stateOutdated = 'outdated'
|
||||
const stateAll = 'all'
|
||||
|
||||
const states = {
|
||||
[stateAll]: {
|
||||
select: T.All,
|
||||
title: T.VMsBackupJob,
|
||||
value: '',
|
||||
},
|
||||
[statBackingUp]: {
|
||||
select: T.VMsBackupJobBackingUpState,
|
||||
title: T.VMsBackupJobBackingUp,
|
||||
value: 'BACKING_UP_VMS',
|
||||
},
|
||||
[stateError]: {
|
||||
select: T.Error,
|
||||
title: T.VMsBackupJobError,
|
||||
value: 'ERROR_VMS',
|
||||
},
|
||||
[stateOutdated]: {
|
||||
select: T.VMsBackupJobOutdatedState,
|
||||
title: T.VMsBackupJobOutdated,
|
||||
value: 'OUTDATED_VMS',
|
||||
},
|
||||
}
|
||||
|
||||
const AlertVmsErrors = ({
|
||||
vmsErrors,
|
||||
message = '',
|
||||
id,
|
||||
vmsOutdated,
|
||||
state,
|
||||
}) => {
|
||||
const [get, { isFetching }] = useLazyGetBackupJobQuery()
|
||||
const [retry] = useRetryBackupJobMutation()
|
||||
const classes = useStyles()
|
||||
|
||||
const handleRetry = () => retry({ id })
|
||||
|
||||
return (
|
||||
<>
|
||||
<Fade in={!!vmsErrors?.ID && state === stateError} unmountOnExit>
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="error"
|
||||
sx={{ gridColumn: 'span 2' }}
|
||||
className={classes.alert}
|
||||
action={
|
||||
<SubmitButton
|
||||
className={classes.submit}
|
||||
onClick={handleRetry}
|
||||
icon={<Translate word={T.Retry} />}
|
||||
tooltip={<Translate word={T.Retry} />}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
</Fade>
|
||||
<Fade in={!!vmsOutdated?.ID && state === stateError} unmountOnExit>
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="warning"
|
||||
sx={{ gridColumn: 'span 2' }}
|
||||
className={classes.alert}
|
||||
action={
|
||||
<SubmitButton
|
||||
className={classes.submit}
|
||||
icon={<RefreshDouble />}
|
||||
tooltip={<Translate word={T.Refresh} />}
|
||||
isSubmitting={isFetching}
|
||||
onClick={() => get({ id })}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Translate word={T.BackupJobRefresh} />
|
||||
</Alert>
|
||||
</Fade>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders mainly information tab.
|
||||
*
|
||||
* @param {object} props - Props
|
||||
* @param {string} props.id - backup job id
|
||||
* @returns {ReactElement} Information tab
|
||||
*/
|
||||
const VmsInfoTab = ({ id }) => {
|
||||
const [state, setState] = useState(stateAll)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const path = PATH.INSTANCE.VMS.DETAIL
|
||||
const history = useHistory()
|
||||
|
||||
const handleRowClick = (rowId) => {
|
||||
history.push(generatePath(path, { id: String(rowId) }))
|
||||
}
|
||||
|
||||
const { data: backupJobData } = useGetBackupJobQuery({ id })
|
||||
const { TEMPLATE, ERROR_VMS, OUTDATED_VMS, ID } = backupJobData
|
||||
|
||||
const handleChangeState = (evt) => setState(evt.target.value)
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl>
|
||||
<Paper
|
||||
variant="outlined"
|
||||
sx={{ height: 'fit-content' }}
|
||||
className={classes.box}
|
||||
>
|
||||
<List>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography noWrap>
|
||||
<Translate word={T.FilterBy} />
|
||||
</Typography>
|
||||
</ListItem>
|
||||
<ListItem className={classes.center}>
|
||||
<RadioGroup
|
||||
row
|
||||
aria-labelledby="filter_vms"
|
||||
value={state}
|
||||
onChange={handleChangeState}
|
||||
>
|
||||
{Object.keys(states).map((type) => (
|
||||
<FormControlLabel
|
||||
className={state === type ? classes.checked : ''}
|
||||
key={type}
|
||||
value={type}
|
||||
control={<Radio />}
|
||||
label={states[type].select}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
</FormControl>
|
||||
|
||||
<Paper
|
||||
variant="outlined"
|
||||
sx={{ height: 'fit-content' }}
|
||||
className={classes.box}
|
||||
>
|
||||
<List>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography noWrap>
|
||||
<Translate word={states?.[state]?.title || ''} />
|
||||
</Typography>
|
||||
<AttachVms id={ID} template={TEMPLATE} />
|
||||
</ListItem>
|
||||
<ListItem className={classes.stretch}>
|
||||
<AlertVmsErrors
|
||||
vmsErrors={ERROR_VMS}
|
||||
vmsOutdated={OUTDATED_VMS}
|
||||
message={TEMPLATE?.ERROR}
|
||||
id={ID}
|
||||
state={state}
|
||||
/>
|
||||
<VmsTable
|
||||
disableRowSelect
|
||||
disableGlobalSort
|
||||
backupjobs={backupJobData}
|
||||
backupjobsState={states?.[state]?.value || ''}
|
||||
onRowClick={(row) => handleRowClick(row.ID)}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
AlertVmsErrors.propTypes = {
|
||||
vmsErrors: PropTypes.object,
|
||||
message: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
vmsOutdated: PropTypes.object,
|
||||
state: PropTypes.string,
|
||||
}
|
||||
|
||||
AlertVmsErrors.displayName = 'AlertVmsErrors'
|
||||
|
||||
VmsInfoTab.propTypes = {
|
||||
tabProps: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
}
|
||||
|
||||
VmsInfoTab.displayName = 'VmsInfoTab'
|
||||
|
||||
export default VmsInfoTab
|
65
src/fireedge/src/client/components/Tabs/BackupJobs/index.js
Normal file
65
src/fireedge/src/client/components/Tabs/BackupJobs/index.js
Normal file
@ -0,0 +1,65 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Alert, LinearProgress } from '@mui/material'
|
||||
import PropTypes from 'prop-types'
|
||||
import { memo, useMemo } from 'react'
|
||||
|
||||
import { RESOURCE_NAMES } from 'client/constants'
|
||||
import { useViews } from 'client/features/Auth'
|
||||
import { useGetBackupJobQuery } from 'client/features/OneApi/backupjobs'
|
||||
import { getAvailableInfoTabs } from 'client/models/Helper'
|
||||
|
||||
import Tabs from 'client/components/Tabs'
|
||||
import Info from 'client/components/Tabs/BackupJobs/Info'
|
||||
import SchedActions from 'client/components/Tabs/BackupJobs/SchedActions'
|
||||
import VMs from 'client/components/Tabs/BackupJobs/VMs'
|
||||
|
||||
const getTabComponent = (tabName) =>
|
||||
({
|
||||
info: Info,
|
||||
vms: VMs,
|
||||
sched_actions: SchedActions,
|
||||
}[tabName])
|
||||
|
||||
const BackupJobTabs = memo(({ id }) => {
|
||||
const { view, getResourceView } = useViews()
|
||||
const { isError, error, status, data } = useGetBackupJobQuery({ id })
|
||||
|
||||
const tabsAvailable = useMemo(() => {
|
||||
const resource = RESOURCE_NAMES.BACKUPJOBS
|
||||
const infoTabs = getResourceView(resource)?.['info-tabs'] ?? {}
|
||||
|
||||
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
|
||||
}, [view, id])
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Alert severity="error" variant="outlined">
|
||||
{error.data}
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'fulfilled' || id === data?.ID) {
|
||||
return <Tabs addBorder tabs={tabsAvailable ?? []} />
|
||||
}
|
||||
|
||||
return <LinearProgress color="secondary" sx={{ width: '100%' }} />
|
||||
})
|
||||
BackupJobTabs.propTypes = { id: PropTypes.string.isRequired }
|
||||
BackupJobTabs.displayName = 'BackupJobTabs'
|
||||
|
||||
export default BackupJobTabs
|
38
src/fireedge/src/client/constants/backupjob.js
Normal file
38
src/fireedge/src/client/constants/backupjob.js
Normal file
@ -0,0 +1,38 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as ACTIONS from 'client/constants/actions'
|
||||
|
||||
/**
|
||||
* @typedef BackupJob
|
||||
* @property {string|number} ID - Id
|
||||
* @property {string} NAME - Name
|
||||
* @property {object} TEMPLATE - Template information
|
||||
* @property {string} [TEMPLATE.DESCRIPTION] - BackupJob Description
|
||||
*/
|
||||
|
||||
export const BACKUPJOB_ACTIONS = {
|
||||
CREATE_DIALOG: 'create_dialog',
|
||||
UPDATE_DIALOG: 'update_dialog',
|
||||
DELETE: 'delete',
|
||||
RENAME: ACTIONS.RENAME,
|
||||
CHANGE_OWNER: ACTIONS.CHANGE_OWNER,
|
||||
CHANGE_GROUP: ACTIONS.CHANGE_GROUP,
|
||||
PRIORITY: 'priority',
|
||||
LOCK: 'lock',
|
||||
UNLOCK: 'unlock',
|
||||
START: 'start',
|
||||
CANCEL: 'cancel',
|
||||
}
|
@ -181,11 +181,14 @@ export const RESOURCE_NAMES = {
|
||||
SERVICE: 'service',
|
||||
SERVICE_TEMPLATE: 'service-template',
|
||||
ZONE: 'zone',
|
||||
BACKUPJOBS: 'backupjobs',
|
||||
}
|
||||
|
||||
export * as T from 'client/constants/translates'
|
||||
|
||||
export * as ACTIONS from 'client/constants/actions'
|
||||
export * as STATES from 'client/constants/states'
|
||||
|
||||
export * from 'client/constants/backupjob'
|
||||
export * from 'client/constants/cluster'
|
||||
export * from 'client/constants/common'
|
||||
export * from 'client/constants/datastore'
|
||||
|
@ -87,6 +87,7 @@ module.exports = {
|
||||
CreateVmTemplate: 'Create VM Template',
|
||||
CreateVDC: 'Create VDC',
|
||||
UpdateVDC: 'Update VDC',
|
||||
CreateBackupJob: 'Create BackupJob',
|
||||
CurrentGroup: 'Current group: %s',
|
||||
CurrentOwner: 'Current owner: %s',
|
||||
Delete: 'Delete',
|
||||
@ -180,6 +181,7 @@ module.exports = {
|
||||
SelectTheNewGroup: 'Select the new group',
|
||||
SelectTheNewOwner: 'Select the new owner',
|
||||
SelectTheNewSecurityGroup: 'Select the new security group',
|
||||
SelectVms: 'Select VMs',
|
||||
SelectVmTemplate: 'Select a VM Template',
|
||||
SelectYourActiveGroup: 'Select your active group',
|
||||
Share: 'Share',
|
||||
@ -280,6 +282,7 @@ module.exports = {
|
||||
PasswordsMustMatch: 'Passwords must match',
|
||||
Token2FA: '2FA Token',
|
||||
KeepLoggedIn: 'Keep me logged in',
|
||||
KeepLast: 'Keep Last',
|
||||
Credentials: 'Credentials',
|
||||
SwitchView: 'Switch view',
|
||||
SwitchGroup: 'Switch group',
|
||||
@ -408,6 +411,8 @@ module.exports = {
|
||||
Backups: 'Backups',
|
||||
BackupDatastore: 'Backup Datastore',
|
||||
BackupRestored: 'Backup restored',
|
||||
BackupJobRefresh:
|
||||
'There are machines in outdated, you can refresh to see if the backups are already done',
|
||||
Datastore: 'Datastore',
|
||||
Datastores: 'Datastores',
|
||||
Image: 'Image',
|
||||
@ -444,6 +449,8 @@ module.exports = {
|
||||
ResetBackup: 'Reset',
|
||||
IncrementId: 'Increment ID',
|
||||
RestoreBackup: 'Restore backup',
|
||||
BackupJobs: 'BackupJobs',
|
||||
BackupJob: 'BackupJob',
|
||||
|
||||
/* storage backends */
|
||||
StorageBackend: 'Storage backend',
|
||||
@ -484,6 +491,7 @@ module.exports = {
|
||||
ResticPassword: 'Restic password',
|
||||
ResticSFTPUser: 'Restic SFTP user',
|
||||
ResticSFTPServer: 'Restic SFTP server',
|
||||
Priority: 'Priority',
|
||||
BackupIOPriority: 'Backup I/O priority',
|
||||
BackupIOPriorityConcept:
|
||||
'Run restic operations under a given ionice priority using the best-effort I/O scheduler',
|
||||
@ -569,6 +577,13 @@ module.exports = {
|
||||
Instances: 'Instances',
|
||||
VM: 'VM',
|
||||
VMs: 'VMs',
|
||||
VMsBackupJob: 'VMs in BackupJob',
|
||||
VMsBackupJobError: 'VMs in error',
|
||||
VMsBackupJobBackingUp: 'VMs Backing Up',
|
||||
VMsBackupJobOutdated: 'VMs Outdated',
|
||||
VMsBackupJobBackingUpState: 'Backing Up',
|
||||
VMsBackupJobOutdatedState: 'Outdated',
|
||||
VMsBackupJobOrder: 'VM List (ordered)',
|
||||
VirtualRouter: 'Virtual Router',
|
||||
VirtualRouters: 'Virtual Routers',
|
||||
VMGroup: 'VM Group',
|
||||
@ -611,6 +626,8 @@ module.exports = {
|
||||
RegistrationTime: 'Registration time',
|
||||
StartTime: 'Start time',
|
||||
Started: 'Started',
|
||||
NotStartedYet: 'Not started yet',
|
||||
OnGoing: 'On Going',
|
||||
StartedOnTime: 'Started on %s',
|
||||
Total: 'Total',
|
||||
Prolog: 'Prolog',
|
||||
@ -1198,6 +1215,7 @@ module.exports = {
|
||||
UsedLeases: 'Used leases',
|
||||
TotalLeases: 'Total leases',
|
||||
TotalClusters: 'Total clusters',
|
||||
Completed: 'Completed',
|
||||
RecoverNetworkDescription: `
|
||||
Recovers a Virtual Network in ERROR state or waiting for a driver operation to complete.
|
||||
The recovery may be done by failing, succeeding or retrying the current operation.
|
||||
@ -1307,6 +1325,9 @@ module.exports = {
|
||||
/* Marketplace App schema */
|
||||
/* Marketplace App - general */
|
||||
RegisteredAt: 'Registered %s',
|
||||
LastBackupTime: 'Last Backup Time: %s',
|
||||
LastBackupTimeInfo: 'Last Backup Time',
|
||||
LastBackupDuration: 'Last Backup Duration',
|
||||
Version: 'Version',
|
||||
AppTemplate: 'App Template',
|
||||
TemplatesForTheApp: 'Templates for the App',
|
||||
|
59
src/fireedge/src/client/containers/BackupJobs/Create.js
Normal file
59
src/fireedge/src/client/containers/BackupJobs/Create.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import { useHistory } from 'react-router'
|
||||
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import {
|
||||
DefaultFormStepper,
|
||||
SkeletonStepsForm,
|
||||
} from 'client/components/FormStepper'
|
||||
import { CreateForm } from 'client/components/Forms/BackupJob'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { useCreateBackupJobMutation } from 'client/features/OneApi/backupjobs'
|
||||
import { jsonToXml } from 'client/models/Helper'
|
||||
|
||||
/**
|
||||
* Displays the creation or modification form to a BackupJob.
|
||||
*
|
||||
* @returns {ReactElement} Backup Job form
|
||||
*/
|
||||
function CreateBackupJob() {
|
||||
const history = useHistory()
|
||||
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
const [create] = useCreateBackupJobMutation()
|
||||
|
||||
const onSubmit = async (template) => {
|
||||
try {
|
||||
const newBackupJobId = await create({
|
||||
template: jsonToXml(template),
|
||||
}).unwrap()
|
||||
if (newBackupJobId) {
|
||||
history.push(PATH.STORAGE.BACKUPJOBS.LIST)
|
||||
enqueueSuccess(`BackupJob created - #${newBackupJobId}`)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
<CreateForm onSubmit={onSubmit} fallback={<SkeletonStepsForm />}>
|
||||
{(config) => <DefaultFormStepper {...config} />}
|
||||
</CreateForm>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateBackupJob
|
36
src/fireedge/src/client/containers/BackupJobs/Detail.js
Normal file
36
src/fireedge/src/client/containers/BackupJobs/Detail.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { ReactElement } from 'react'
|
||||
import { useParams, Redirect } from 'react-router-dom'
|
||||
|
||||
import VDCTabs from 'client/components/Tabs/Vdc'
|
||||
|
||||
/**
|
||||
* Displays the detail information about a VDC.
|
||||
*
|
||||
* @returns {ReactElement} VDC detail component.
|
||||
*/
|
||||
function VDCDetail() {
|
||||
const { id } = useParams()
|
||||
|
||||
if (Number.isNaN(+id)) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
return <VDCTabs id={id} />
|
||||
}
|
||||
|
||||
export default VDCDetail
|
162
src/fireedge/src/client/containers/BackupJobs/index.js
Normal file
162
src/fireedge/src/client/containers/BackupJobs/index.js
Normal file
@ -0,0 +1,162 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Box, Chip, Stack, Typography } from '@mui/material'
|
||||
import { Cancel, Pin as GotoIcon, RefreshDouble } from 'iconoir-react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactElement, memo, useState } from 'react'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { SubmitButton } from 'client/components/FormControl'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import { BackupJobsTable } from 'client/components/Tables'
|
||||
|
||||
import BackupJobActions from 'client/components/Tables/BackupJobs/actions'
|
||||
import BackupJobsTabs from 'client/components/Tabs/BackupJobs'
|
||||
import { T } from 'client/constants'
|
||||
import {
|
||||
useLazyGetBackupJobQuery,
|
||||
useUpdateBackupJobMutation,
|
||||
} from 'client/features/OneApi/backupjobs'
|
||||
|
||||
/**
|
||||
* Displays a list of Backup Jobs with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Backup Jobs list and selected row(s)
|
||||
*/
|
||||
function BackupJobs() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = BackupJobActions()
|
||||
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
|
||||
return (
|
||||
<SplitPane gridTemplateRows="1fr auto 1fr">
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box height={1} {...(hasSelectedRows && getGridProps())}>
|
||||
<BackupJobsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
useUpdateMutation={useUpdateBackupJobMutation}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs
|
||||
template={selectedRows[0]?.original}
|
||||
gotoPage={selectedRows[0]?.gotoPage}
|
||||
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Backup Job Template.
|
||||
*
|
||||
* @param {object} template - Backup Job Template id to display
|
||||
* @param {Function} [gotoPage] - Function to navigate to a page of a Backup Job Template
|
||||
* @param {Function} [unselect] - Function to unselect a Backup Job Template
|
||||
* @returns {ReactElement} Backup Job Template details
|
||||
*/
|
||||
const InfoTabs = memo(({ template, gotoPage, unselect }) => {
|
||||
const [getBackupJob, { data, isFetching }] = useLazyGetBackupJobQuery()
|
||||
const id = data?.ID ?? template.ID
|
||||
const name = data?.NAME ?? template.NAME
|
||||
|
||||
return (
|
||||
<Stack overflow="auto">
|
||||
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
|
||||
<Typography color="text.primary" noWrap flexGrow={1}>
|
||||
{`#${id} | ${name}`}
|
||||
</Typography>
|
||||
|
||||
{/* -- ACTIONS -- */}
|
||||
<SubmitButton
|
||||
data-cy="detail-refresh"
|
||||
icon={<RefreshDouble />}
|
||||
tooltip={Tr(T.Refresh)}
|
||||
isSubmitting={isFetching}
|
||||
onClick={() => getBackupJob({ id })}
|
||||
/>
|
||||
{typeof gotoPage === 'function' && (
|
||||
<SubmitButton
|
||||
data-cy="locate-on-table"
|
||||
icon={<GotoIcon />}
|
||||
tooltip={Tr(T.LocateOnTable)}
|
||||
onClick={() => gotoPage()}
|
||||
/>
|
||||
)}
|
||||
{typeof unselect === 'function' && (
|
||||
<SubmitButton
|
||||
data-cy="unselect"
|
||||
icon={<Cancel />}
|
||||
tooltip={Tr(T.Close)}
|
||||
onClick={() => unselect()}
|
||||
/>
|
||||
)}
|
||||
{/* -- END ACTIONS -- */}
|
||||
</Stack>
|
||||
<BackupJobsTabs id={id} />
|
||||
</Stack>
|
||||
)
|
||||
})
|
||||
|
||||
InfoTabs.propTypes = {
|
||||
template: PropTypes.object,
|
||||
gotoPage: PropTypes.func,
|
||||
unselect: PropTypes.func,
|
||||
}
|
||||
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onClick={gotoPage}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default BackupJobs
|
493
src/fireedge/src/client/features/OneApi/backupjobs.js
Normal file
493
src/fireedge/src/client/features/OneApi/backupjobs.js
Normal file
@ -0,0 +1,493 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Actions, Commands } from 'server/utils/constants/commands/backupjobs'
|
||||
|
||||
import { FilterFlag, Permission } from 'client/constants'
|
||||
import {
|
||||
ONE_RESOURCES,
|
||||
ONE_RESOURCES_POOL,
|
||||
oneApi,
|
||||
} from 'client/features/OneApi'
|
||||
import {
|
||||
removeResourceOnPool,
|
||||
updateNameOnResource,
|
||||
updateResourceOnPool,
|
||||
updateTemplateOnResource,
|
||||
} from 'client/features/OneApi/common'
|
||||
|
||||
const { BACKUPJOB } = ONE_RESOURCES
|
||||
const { BACKUPJOB_POOL } = ONE_RESOURCES_POOL
|
||||
|
||||
const backupjobApi = oneApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getBackupJobs: builder.query({
|
||||
/**
|
||||
* Retrieves information for all or part of the Resources in the pool.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {FilterFlag} [params.filter] - Filter flag
|
||||
* @param {number} [params.start] - Range start ID
|
||||
* @param {number} [params.end] - Range end ID
|
||||
* @returns {Array[Object]} List of Backup Jobs
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_POOL_INFO
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
transformResponse: (data) =>
|
||||
[data?.BACKUPJOB_POOL?.BACKUPJOB ?? []].flat(),
|
||||
providesTags: (backupjobs) =>
|
||||
backupjobs
|
||||
? [
|
||||
...backupjobs.map(({ ID }) => ({
|
||||
type: BACKUPJOB_POOL,
|
||||
id: `${ID}`,
|
||||
})),
|
||||
BACKUPJOB_POOL,
|
||||
]
|
||||
: [BACKUPJOB_POOL],
|
||||
}),
|
||||
getBackupJob: builder.query({
|
||||
/**
|
||||
* Retrieves information for the BackupJob.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string} params.id - BackupJob id
|
||||
* @param {boolean} [params.decrypt] - True to decrypt contained secrets (only admin)
|
||||
* @returns {object} Get VDC identified by id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_INFO
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
transformResponse: (data) => data?.BACKUPJOB ?? {},
|
||||
providesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
async onQueryStarted({ id }, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const { data: resourceFromQuery } = await queryFulfilled
|
||||
|
||||
dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJobs',
|
||||
undefined,
|
||||
updateResourceOnPool({ id, resourceFromQuery })
|
||||
)
|
||||
)
|
||||
} catch {
|
||||
// if the query fails, we want to remove the resource from the pool
|
||||
dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJobs',
|
||||
undefined,
|
||||
removeResourceOnPool({ id })
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
}),
|
||||
allocateBackupJob: builder.mutation({
|
||||
/**
|
||||
* Allocates a new backup job in OpenNebula.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.template - Template for the new backupjob
|
||||
* @returns {number} The allocated backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_ALLOCATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [BACKUPJOB_POOL],
|
||||
}),
|
||||
removeBackupJob: builder.mutation({
|
||||
/**
|
||||
* Deletes the given Backup Job from the pool.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {number|string} params.id - Backup Job id
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_DELETE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: [BACKUPJOB_POOL],
|
||||
}),
|
||||
createBackupJob: builder.mutation({
|
||||
/**
|
||||
* Creates a new BackupJob in OpenNebula.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {string} params.template - Backup Job Template
|
||||
* @returns {number} BackupJob id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_ALLOCATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
}),
|
||||
updateBackupJob: builder.mutation({
|
||||
/**
|
||||
* Replaces the template contents.
|
||||
*
|
||||
* @param {object} params - Request params
|
||||
* @param {number|string} params.id - Backup Job id
|
||||
* @param {string} params.template - The new template contents
|
||||
* @param {0|1} params.replace
|
||||
* - Update type:
|
||||
* ``0``: Replace the whole template.
|
||||
* ``1``: Merge new template with the existing one.
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_UPDATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const patchBackupJob = dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJob',
|
||||
{ id: params.id },
|
||||
updateTemplateOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
const patchBackupJobs = dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJobs',
|
||||
undefined,
|
||||
updateTemplateOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
queryFulfilled.catch(() => {
|
||||
patchBackupJob.undo()
|
||||
patchBackupJobs.undo()
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
renameBackupJob: builder.mutation({
|
||||
/**
|
||||
* Renames a Backup Job.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job id
|
||||
* @param {string} params.name - The new name
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_RENAME
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
async onQueryStarted(params, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
const patchBackupJob = dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJob',
|
||||
{ id: params.id },
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
const patchBackupJobs = dispatch(
|
||||
backupjobApi.util.updateQueryData(
|
||||
'getBackupJobs',
|
||||
undefined,
|
||||
updateNameOnResource(params)
|
||||
)
|
||||
)
|
||||
|
||||
queryFulfilled.catch(() => {
|
||||
patchBackupJob.undo()
|
||||
patchBackupJobs.undo()
|
||||
})
|
||||
} catch {}
|
||||
},
|
||||
}),
|
||||
changeBackupJobOwnership: builder.mutation({
|
||||
/**
|
||||
* Changes the ownership of a Backup Job.
|
||||
* If set `user` or `group` to -1, it's not changed.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job id
|
||||
* @param {string|number|'-1'} [params.userId] - User id
|
||||
* @param {Permission|'-1'} [params.groupId] - Group id
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_CHOWN
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: BACKUPJOB, id },
|
||||
BACKUPJOB_POOL,
|
||||
],
|
||||
}),
|
||||
lockBackupJob: builder.mutation({
|
||||
/**
|
||||
* Locks an backup job. Lock certain actions depending on blocking level.
|
||||
* - `USE` (1): locks Admin, Manage and Use actions.
|
||||
* - `MANAGE` (2): locks Manage and Use actions.
|
||||
* - `ADMIN` (3): locks only Admin actions.
|
||||
* - `ALL` (4): locks all actions.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup job id
|
||||
* @param {'1'|'2'|'3'|'4'} params.lock - Lock level
|
||||
* @returns {number} Backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_LOCK
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: BACKUPJOB, id },
|
||||
BACKUPJOB_POOL,
|
||||
],
|
||||
}),
|
||||
unlockBackupJob: builder.mutation({
|
||||
/**
|
||||
* Unlocks an Backupjob.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup job id
|
||||
* @returns {number} Backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_UNLOCK
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [
|
||||
{ type: BACKUPJOB, id },
|
||||
BACKUPJOB_POOL,
|
||||
],
|
||||
}),
|
||||
startBackupJob: builder.mutation({
|
||||
/**
|
||||
* Start an Backupjob.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup job id
|
||||
* @returns {number} Backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_BACKUP
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
cancelBackupJob: builder.mutation({
|
||||
/**
|
||||
* Cancel an Backupjob.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup job id
|
||||
* @returns {number} Backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_CANCEL
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
retryBackupJob: builder.mutation({
|
||||
/**
|
||||
* Retry an Backupjob.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup job id
|
||||
* @returns {number} Backup job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_RETRY
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
changeBackupJobPermissions: builder.mutation({
|
||||
/**
|
||||
* Changes the permission bits of a Backup Job.
|
||||
* If set any permission to -1, it's not changed.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job 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} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_CHMOD
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
addScheduledActionBackupJob: builder.mutation({
|
||||
/**
|
||||
* Add scheduled action to Backup Job.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - BackupJob id
|
||||
* @param {string} params.template - Template containing the new scheduled action
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_SCHED_ADD
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
updateScheduledActionBackupJob: builder.mutation({
|
||||
/**
|
||||
* Update scheduled action to Backup Job.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job id
|
||||
* @param {string} params.schedId - The ID of the scheduled action
|
||||
* @param {string} params.template - Template containing the updated scheduled action
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_SCHED_UPDATE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
deleteScheduledActionBackupJob: builder.mutation({
|
||||
/**
|
||||
* Delete scheduled action to Backup Job.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job id
|
||||
* @param {string} params.schedId - The ID of the scheduled action
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_SCHED_DELETE
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
updatePriorityBackupJob: builder.mutation({
|
||||
/**
|
||||
* Update Priority to Backup Job.
|
||||
*
|
||||
* @param {object} params - Request parameters
|
||||
* @param {string|number} params.id - Backup Job id
|
||||
* @param {number} params.priority - priority number
|
||||
* @returns {number} Backup Job id
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
query: (params) => {
|
||||
const name = Actions.BACKUPJOB_PRIORITY
|
||||
const command = { name, ...Commands[name] }
|
||||
|
||||
return { params, command }
|
||||
},
|
||||
invalidatesTags: (_, __, { id }) => [{ type: BACKUPJOB, id }],
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const {
|
||||
// Queries
|
||||
useGetBackupJobsQuery,
|
||||
useLazyGetBackupJobsQuery,
|
||||
useGetBackupJobQuery,
|
||||
useLazyGetBackupJobQuery,
|
||||
|
||||
// Mutations
|
||||
useAllocateBackupJobMutation,
|
||||
useUpdatePriorityBackupJobMutation,
|
||||
useRemoveBackupJobMutation,
|
||||
useCreateBackupJobMutation,
|
||||
useUpdateBackupJobMutation,
|
||||
useRenameBackupJobMutation,
|
||||
useChangeBackupJobOwnershipMutation,
|
||||
useLockBackupJobMutation,
|
||||
useUnlockBackupJobMutation,
|
||||
useStartBackupJobMutation,
|
||||
useCancelBackupJobMutation,
|
||||
useRetryBackupJobMutation,
|
||||
useChangeBackupJobPermissionsMutation,
|
||||
useAddScheduledActionBackupJobMutation,
|
||||
useUpdateScheduledActionBackupJobMutation,
|
||||
useDeleteScheduledActionBackupJobMutation,
|
||||
} = backupjobApi
|
||||
|
||||
export default backupjobApi
|
@ -23,6 +23,7 @@ import { httpCodes } from 'server/utils/constants'
|
||||
const ONE_RESOURCES = {
|
||||
ACL: 'ACL',
|
||||
APP: 'APP',
|
||||
BACKUPJOB: 'BACKUPJOB',
|
||||
CLUSTER: 'CLUSTER',
|
||||
DATASTORE: 'DATASTORE',
|
||||
FILE: 'FILE',
|
||||
|
@ -494,3 +494,19 @@ export const getUnknownAttributes = (obj = {}, knownAttributes) => {
|
||||
|
||||
return unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the Ids for values selected in datatables.
|
||||
*
|
||||
* @param {any} arr - Data for Datatables.
|
||||
* @returns {string} Returns string with ids.
|
||||
*/
|
||||
export const extractIDValues = (arr = []) => {
|
||||
const dataArray = Array.isArray(arr) ? arr : [arr]
|
||||
const idValues = dataArray
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
.filter((obj) => obj.hasOwnProperty('ID'))
|
||||
.map((obj) => obj.ID)
|
||||
|
||||
return idValues.join(',')
|
||||
}
|
||||
|
333
src/fireedge/src/server/utils/constants/commands/backupjobs.js
Normal file
333
src/fireedge/src/server/utils/constants/commands/backupjobs.js
Normal file
@ -0,0 +1,333 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2023, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const {
|
||||
from: { resource, postBody, query },
|
||||
httpMethod: { GET, POST, PUT, DELETE },
|
||||
} = require('../defaults')
|
||||
|
||||
const baseCommand = 'backupjob'
|
||||
const baseCommandPool = `${baseCommand}pool`
|
||||
|
||||
const BACKUPJOB_ALLOCATE = `${baseCommand}.allocate`
|
||||
const BACKUPJOB_DELETE = `${baseCommand}.delete`
|
||||
const BACKUPJOB_INFO = `${baseCommand}.info`
|
||||
const BACKUPJOB_UPDATE = `${baseCommand}.update`
|
||||
const BACKUPJOB_RENAME = `${baseCommand}.rename`
|
||||
const BACKUPJOB_CHOWN = `${baseCommand}.chown`
|
||||
const BACKUPJOB_CHMOD = `${baseCommand}.chmod`
|
||||
const BACKUPJOB_LOCK = `${baseCommand}.lock`
|
||||
const BACKUPJOB_UNLOCK = `${baseCommand}.unlock`
|
||||
const BACKUPJOB_BACKUP = `${baseCommand}.backup`
|
||||
const BACKUPJOB_CANCEL = `${baseCommand}.cancel`
|
||||
const BACKUPJOB_RETRY = `${baseCommand}.retry`
|
||||
const BACKUPJOB_PRIORITY = `${baseCommand}.priority`
|
||||
const BACKUPJOB_SCHED_ADD = `${baseCommand}.schedadd`
|
||||
const BACKUPJOB_SCHED_DELETE = `${baseCommand}.scheddelete`
|
||||
const BACKUPJOB_SCHED_UPDATE = `${baseCommand}.schedupdate`
|
||||
const BACKUPJOB_POOL_INFO = `${baseCommandPool}.info`
|
||||
|
||||
const Actions = {
|
||||
BACKUPJOB_ALLOCATE,
|
||||
BACKUPJOB_DELETE,
|
||||
BACKUPJOB_INFO,
|
||||
BACKUPJOB_UPDATE,
|
||||
BACKUPJOB_RENAME,
|
||||
BACKUPJOB_CHOWN,
|
||||
BACKUPJOB_CHMOD,
|
||||
BACKUPJOB_LOCK,
|
||||
BACKUPJOB_UNLOCK,
|
||||
BACKUPJOB_BACKUP,
|
||||
BACKUPJOB_CANCEL,
|
||||
BACKUPJOB_RETRY,
|
||||
BACKUPJOB_PRIORITY,
|
||||
BACKUPJOB_SCHED_ADD,
|
||||
BACKUPJOB_SCHED_DELETE,
|
||||
BACKUPJOB_SCHED_UPDATE,
|
||||
BACKUPJOB_POOL_INFO,
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Actions,
|
||||
Commands: {
|
||||
[BACKUPJOB_ALLOCATE]: {
|
||||
// inspected
|
||||
httpMethod: POST,
|
||||
params: {
|
||||
template: {
|
||||
from: postBody,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_DELETE]: {
|
||||
// inspected
|
||||
httpMethod: DELETE,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_INFO]: {
|
||||
// inspected
|
||||
httpMethod: GET,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: -1,
|
||||
},
|
||||
decrypt: {
|
||||
from: query,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_UPDATE]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
template: {
|
||||
from: postBody,
|
||||
default: '',
|
||||
},
|
||||
replace: {
|
||||
from: postBody,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_RENAME]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
from: postBody,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_CHOWN]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
user: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
group: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_CHMOD]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
ownerUse: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
ownerManage: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
ownerAdmin: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
groupUse: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
groupManage: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
groupAdmin: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
otherUse: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
otherManage: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
otherAdmin: {
|
||||
from: postBody,
|
||||
default: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_LOCK]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
level: {
|
||||
from: postBody,
|
||||
default: 4,
|
||||
},
|
||||
test: {
|
||||
from: postBody,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_UNLOCK]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_BACKUP]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_CANCEL]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_RETRY]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_PRIORITY]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
priority: {
|
||||
from: postBody,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_SCHED_ADD]: {
|
||||
// inspected
|
||||
httpMethod: POST,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
template: {
|
||||
from: postBody,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_SCHED_DELETE]: {
|
||||
// inspected
|
||||
httpMethod: DELETE,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
schedId: {
|
||||
from: postBody,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_SCHED_UPDATE]: {
|
||||
// inspected
|
||||
httpMethod: PUT,
|
||||
params: {
|
||||
id: {
|
||||
from: resource,
|
||||
default: 0,
|
||||
},
|
||||
schedId: {
|
||||
from: postBody,
|
||||
default: 0,
|
||||
},
|
||||
template: {
|
||||
from: postBody,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
[BACKUPJOB_POOL_INFO]: {
|
||||
// inspected
|
||||
httpMethod: GET,
|
||||
params: {
|
||||
filter: {
|
||||
from: query,
|
||||
default: -2,
|
||||
},
|
||||
start: {
|
||||
from: query,
|
||||
default: -1,
|
||||
},
|
||||
end: {
|
||||
from: query,
|
||||
default: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
const { Commands: acl } = require('./acl')
|
||||
const { Commands: backupjobs } = require('./backupjobs')
|
||||
const { Commands: cluster } = require('./cluster')
|
||||
const { Commands: datastore } = require('./datastore')
|
||||
const { Commands: document } = require('./document')
|
||||
@ -38,6 +39,7 @@ const { Commands: zone } = require('./zone')
|
||||
|
||||
module.exports = {
|
||||
...acl,
|
||||
...backupjobs,
|
||||
...cluster,
|
||||
...datastore,
|
||||
...document,
|
||||
|
Loading…
Reference in New Issue
Block a user