chore(xo-web/backup-ng/new): better "settings" management (#3259)
This commit is contained in:
parent
cc90d41be4
commit
0ca7b8539f
@ -81,7 +81,7 @@
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-watch": "^5.0.0",
|
||||
"human-format": "^0.10.0",
|
||||
"immutable": "^3.8.2",
|
||||
"immutable": "^4.0.0-rc.9",
|
||||
"index-modules": "^0.3.0",
|
||||
"is-ip": "^2.0.0",
|
||||
"jsonrpc-websocket-client": "^0.3.0",
|
||||
|
@ -8,29 +8,20 @@ import renderXoItem, { renderXoItemFromId } from 'render-xo-item'
|
||||
import Select from 'form/select'
|
||||
import Tooltip from 'tooltip'
|
||||
import Upgrade from 'xoa-upgrade'
|
||||
import { Card, CardBlock, CardHeader } from 'card'
|
||||
import { constructSmartPattern, destructSmartPattern } from 'smart-backup'
|
||||
import { Container, Col, Row } from 'grid'
|
||||
import { flatten, includes, isEmpty, keyBy, map, mapValues, some } from 'lodash'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
import { Map } from 'immutable'
|
||||
import { Number } from 'form'
|
||||
import { SelectRemote, SelectSr, SelectVm } from 'select-objects'
|
||||
import {
|
||||
addSubscriptions,
|
||||
generateRandomId,
|
||||
resolveId,
|
||||
resolveIds,
|
||||
} from 'utils'
|
||||
import { Card, CardBlock, CardHeader } from 'card'
|
||||
import { constructSmartPattern, destructSmartPattern } from 'smart-backup'
|
||||
import { Container, Col, Row } from 'grid'
|
||||
import { injectState, provideState } from '@julien-f/freactal'
|
||||
import { Number } from 'form'
|
||||
import { SelectRemote, SelectSr, SelectVm } from 'select-objects'
|
||||
import {
|
||||
cloneDeep,
|
||||
flatten,
|
||||
forEach,
|
||||
includes,
|
||||
isEmpty,
|
||||
keyBy,
|
||||
map,
|
||||
mapValues,
|
||||
some,
|
||||
} from 'lodash'
|
||||
import {
|
||||
createBackupNgJob,
|
||||
createSchedule,
|
||||
@ -56,35 +47,23 @@ import {
|
||||
|
||||
const normalizeTagValues = values => resolveIds(values).map(value => [value])
|
||||
|
||||
const normalizeCopyRetention = settings => {
|
||||
forEach(settings, schedule => {
|
||||
if (schedule.copyRetention === undefined) {
|
||||
schedule.copyRetention = schedule.exportRetention
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const normalizeSettings = ({
|
||||
settings,
|
||||
exportMode,
|
||||
copyMode,
|
||||
snapshotMode,
|
||||
}) => {
|
||||
forEach(settings, setting => {
|
||||
if (!exportMode) {
|
||||
setting.exportRetention = undefined
|
||||
}
|
||||
|
||||
if (!copyMode) {
|
||||
setting.copyRetention = undefined
|
||||
}
|
||||
|
||||
if (!snapshotMode) {
|
||||
setting.snapshotRetention = undefined
|
||||
}
|
||||
})
|
||||
return settings
|
||||
}
|
||||
const normalizeSettings = ({ settings, exportMode, copyMode, snapshotMode }) =>
|
||||
settings.map(
|
||||
setting =>
|
||||
defined(
|
||||
setting.copyRetention,
|
||||
setting.exportRetention,
|
||||
setting.snapshotRetention
|
||||
) !== undefined
|
||||
? {
|
||||
copyRetention: copyMode ? setting.copyRetention : undefined,
|
||||
exportRetention: exportMode ? setting.exportRetention : undefined,
|
||||
snapshotRetention: snapshotMode
|
||||
? setting.snapshotRetention
|
||||
: undefined,
|
||||
}
|
||||
: setting
|
||||
)
|
||||
|
||||
const constructPattern = values =>
|
||||
values.length === 1
|
||||
@ -127,14 +106,13 @@ const getOptionRenderer = ({ label }) => <span>{_(label)}</span>
|
||||
|
||||
const createDoesRetentionExist = name => {
|
||||
const predicate = setting => setting[name] > 0
|
||||
return ({ settings }) => some(settings, predicate)
|
||||
return ({ propSettings, settings = propSettings }) => settings.some(predicate)
|
||||
}
|
||||
|
||||
const getInitialState = () => ({
|
||||
$pool: {},
|
||||
backupMode: false,
|
||||
compression: true,
|
||||
concurrency: undefined,
|
||||
crMode: false,
|
||||
deltaMode: false,
|
||||
drMode: false,
|
||||
@ -144,19 +122,16 @@ const getInitialState = () => ({
|
||||
inputReportWhenId: generateRandomId(),
|
||||
inputTimeoutId: generateRandomId(),
|
||||
name: '',
|
||||
offlineSnapshot: undefined,
|
||||
paramsUpdated: false,
|
||||
powerState: 'All',
|
||||
remotes: [],
|
||||
reportWhen: undefined,
|
||||
schedules: {},
|
||||
settings: {},
|
||||
settings: undefined,
|
||||
showErrors: false,
|
||||
smartMode: false,
|
||||
snapshotMode: false,
|
||||
srs: [],
|
||||
tags: {},
|
||||
timeout: undefined,
|
||||
tmpSchedule: {},
|
||||
vms: [],
|
||||
})
|
||||
@ -192,20 +167,12 @@ export default [
|
||||
state.schedules,
|
||||
({ id, ...schedule }) => schedule
|
||||
),
|
||||
settings: {
|
||||
...normalizeSettings({
|
||||
settings: cloneDeep(state.settings),
|
||||
exportMode: state.exportMode,
|
||||
copyMode: state.copyMode,
|
||||
snapshotMode: state.snapshotMode,
|
||||
}),
|
||||
'': {
|
||||
concurrency: state.concurrency,
|
||||
offlineSnapshot: state.offlineSnapshot,
|
||||
reportWhen: state.reportWhen,
|
||||
timeout: state.timeout && state.timeout * 1e3,
|
||||
},
|
||||
},
|
||||
settings: normalizeSettings({
|
||||
settings: state.settings,
|
||||
exportMode: state.exportMode,
|
||||
copyMode: state.copyMode,
|
||||
snapshotMode: state.snapshotMode,
|
||||
}).toObject(),
|
||||
remotes:
|
||||
state.deltaMode || state.backupMode
|
||||
? constructPattern(state.remotes)
|
||||
@ -253,7 +220,7 @@ export default [
|
||||
})
|
||||
)
|
||||
|
||||
const settings = cloneDeep(state.settings)
|
||||
let settings = state.settings
|
||||
await Promise.all(
|
||||
map(state.schedules, async schedule => {
|
||||
const tmpId = schedule.id
|
||||
@ -265,41 +232,25 @@ export default [
|
||||
enabled: schedule.enabled,
|
||||
})
|
||||
|
||||
settings[id] = settings[tmpId]
|
||||
delete settings[tmpId]
|
||||
settings = settings.withMutations(settings => {
|
||||
settings.set(id, settings.get(tmpId))
|
||||
settings.delete(tmpId)
|
||||
})
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const globalSettings = props.job.settings[''] || {}
|
||||
const {
|
||||
concurrency = globalSettings.concurrency,
|
||||
offlineSnapshot = globalSettings.offlineSnapshot,
|
||||
reportWhen = globalSettings.reportWhen,
|
||||
timeout,
|
||||
} = state
|
||||
settings[''] = {
|
||||
...globalSettings,
|
||||
reportWhen,
|
||||
concurrency: state.concurrency === '' ? undefined : concurrency,
|
||||
offlineSnapshot,
|
||||
timeout:
|
||||
timeout === ''
|
||||
? undefined
|
||||
: timeout * 1e3 || globalSettings.timeout,
|
||||
}
|
||||
|
||||
await editBackupNgJob({
|
||||
id: props.job.id,
|
||||
name: state.name,
|
||||
mode: state.isDelta ? 'delta' : 'full',
|
||||
compression: state.compression ? 'native' : '',
|
||||
settings: normalizeSettings({
|
||||
settings,
|
||||
settings: settings || state.propSettings,
|
||||
exportMode: state.exportMode,
|
||||
copyMode: state.copyMode,
|
||||
snapshotMode: state.snapshotMode,
|
||||
}),
|
||||
}).toObject(),
|
||||
remotes:
|
||||
state.deltaMode || state.backupMode
|
||||
? constructPattern(state.remotes)
|
||||
@ -366,14 +317,6 @@ export default [
|
||||
const remotes =
|
||||
job.remotes !== undefined ? destructPattern(job.remotes) : []
|
||||
const srs = job.srs !== undefined ? destructPattern(job.srs) : []
|
||||
const settings = cloneDeep(job.settings)
|
||||
delete settings['']
|
||||
const drMode = job.mode === 'full' && !isEmpty(srs)
|
||||
const crMode = job.mode === 'delta' && !isEmpty(srs)
|
||||
|
||||
if (drMode || crMode) {
|
||||
normalizeCopyRetention(settings)
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
@ -387,11 +330,10 @@ export default [
|
||||
),
|
||||
backupMode: job.mode === 'full' && !isEmpty(remotes),
|
||||
deltaMode: job.mode === 'delta' && !isEmpty(remotes),
|
||||
drMode,
|
||||
crMode,
|
||||
drMode: job.mode === 'full' && !isEmpty(srs),
|
||||
crMode: job.mode === 'delta' && !isEmpty(srs),
|
||||
remotes,
|
||||
srs,
|
||||
settings,
|
||||
schedules,
|
||||
...destructVmsPattern(job.vms),
|
||||
}
|
||||
@ -412,38 +354,33 @@ export default [
|
||||
...schedule,
|
||||
},
|
||||
}),
|
||||
deleteSchedule: (_, schedule) => state => {
|
||||
deleteSchedule: (_, schedule) => ({
|
||||
schedules: oldSchedules,
|
||||
propSettings,
|
||||
settings = propSettings,
|
||||
}) => {
|
||||
const id = resolveId(schedule)
|
||||
const schedules = { ...state.schedules }
|
||||
const settings = { ...state.settings }
|
||||
|
||||
const schedules = { ...oldSchedules }
|
||||
delete schedules[id]
|
||||
delete settings[id]
|
||||
return {
|
||||
...state,
|
||||
schedules,
|
||||
settings,
|
||||
settings: settings.delete(id),
|
||||
}
|
||||
},
|
||||
saveSchedule: (
|
||||
_,
|
||||
{
|
||||
copyRetention,
|
||||
cron,
|
||||
exportRetention,
|
||||
name,
|
||||
snapshotRetention,
|
||||
timezone,
|
||||
}
|
||||
) => async (state, props) => {
|
||||
saveSchedule: (_, { cron, timezone, name, ...setting }) => ({
|
||||
editionMode,
|
||||
propSettings,
|
||||
schedules: oldSchedules,
|
||||
settings = propSettings,
|
||||
tmpSchedule,
|
||||
}) => {
|
||||
name = name !== undefined && name.trim() === '' ? undefined : name
|
||||
if (state.editionMode === 'creation') {
|
||||
if (editionMode === 'creation') {
|
||||
const id = generateRandomId()
|
||||
return {
|
||||
...state,
|
||||
editionMode: undefined,
|
||||
schedules: {
|
||||
...state.schedules,
|
||||
...oldSchedules,
|
||||
[id]: {
|
||||
cron,
|
||||
id,
|
||||
@ -451,20 +388,12 @@ export default [
|
||||
timezone,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
...state.settings,
|
||||
[id]: {
|
||||
exportRetention,
|
||||
copyRetention,
|
||||
snapshotRetention,
|
||||
},
|
||||
},
|
||||
settings: settings.set(id, setting),
|
||||
}
|
||||
}
|
||||
|
||||
const id = state.tmpSchedule.id
|
||||
const schedules = { ...state.schedules }
|
||||
const settings = { ...state.settings }
|
||||
const id = tmpSchedule.id
|
||||
const schedules = { ...oldSchedules }
|
||||
|
||||
schedules[id] = {
|
||||
...schedules[id],
|
||||
@ -472,18 +401,11 @@ export default [
|
||||
name,
|
||||
timezone,
|
||||
}
|
||||
settings[id] = {
|
||||
...settings[id],
|
||||
exportRetention,
|
||||
copyRetention,
|
||||
snapshotRetention,
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
editionMode: undefined,
|
||||
schedules,
|
||||
settings,
|
||||
settings: settings.set(id, setting),
|
||||
tmpSchedule: {},
|
||||
}
|
||||
},
|
||||
@ -526,16 +448,42 @@ export default [
|
||||
|
||||
return getInitialState()
|
||||
},
|
||||
setReportWhen: (_, { value }) => () => ({
|
||||
reportWhen: value,
|
||||
}),
|
||||
setConcurrency: (_, concurrency) => (_, { job }) => ({
|
||||
concurrency:
|
||||
concurrency === undefined && job !== undefined ? '' : concurrency,
|
||||
}),
|
||||
setTimeout: (_, timeout) => (_, { job }) => ({
|
||||
timeout: timeout === undefined && job !== undefined ? '' : timeout,
|
||||
setGlobalSettings: (_, { name, value }) => ({
|
||||
propSettings,
|
||||
settings = propSettings,
|
||||
}) => ({
|
||||
settings: settings.update('', setting => ({
|
||||
...setting,
|
||||
[name]: value,
|
||||
})),
|
||||
}),
|
||||
setReportWhen: ({ setGlobalSettings }, { value }) => () => {
|
||||
setGlobalSettings({
|
||||
name: 'reportWhen',
|
||||
value,
|
||||
})
|
||||
},
|
||||
setConcurrency: ({ setGlobalSettings }, value) => () => {
|
||||
setGlobalSettings({
|
||||
name: 'concurrency',
|
||||
value,
|
||||
})
|
||||
},
|
||||
setTimeout: ({ setGlobalSettings }, value) => () => {
|
||||
setGlobalSettings({
|
||||
name: 'timeout',
|
||||
value: value && value * 1e3,
|
||||
})
|
||||
},
|
||||
setOfflineSnapshot: (
|
||||
{ setGlobalSettings },
|
||||
{ target: { checked: value } }
|
||||
) => () => {
|
||||
setGlobalSettings({
|
||||
name: 'offlineSnapshot',
|
||||
value,
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
needUpdateParams: (state, { job, schedules }) =>
|
||||
@ -582,17 +530,14 @@ export default [
|
||||
}),
|
||||
srPredicate: ({ srs }) => sr => isSrWritable(sr) && !includes(srs, sr.id),
|
||||
remotePredicate: ({ remotes }) => ({ id }) => !includes(remotes, id),
|
||||
propSettings: (_, { job }) => Map(get(() => job.settings)),
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ state, effects, remotesById, job }) => {
|
||||
const {
|
||||
concurrency: jobConcurrency,
|
||||
offlineSnapshot: jobOfflineSnapshot,
|
||||
reportWhen: jobReportWhen = 'failure',
|
||||
timeout: jobTimeout,
|
||||
} =
|
||||
get(() => job.settings['']) || {}
|
||||
const { propSettings, settings = propSettings } = state
|
||||
const { concurrency, reportWhen = 'failure', offlineSnapshot, timeout } =
|
||||
settings.get('') || {}
|
||||
|
||||
if (state.needUpdateParams) {
|
||||
effects.updateParams()
|
||||
@ -850,7 +795,7 @@ export default [
|
||||
optionRenderer={getOptionRenderer}
|
||||
options={REPORT_WHEN_FILTER_OPTIONS}
|
||||
required
|
||||
value={state.reportWhen || jobReportWhen}
|
||||
value={reportWhen}
|
||||
valueKey='value'
|
||||
/>
|
||||
</FormGroup>
|
||||
@ -861,7 +806,7 @@ export default [
|
||||
<Number
|
||||
id={state.inputConcurrencyId}
|
||||
onChange={effects.setConcurrency}
|
||||
value={defined(state.concurrency, jobConcurrency)}
|
||||
value={concurrency}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
@ -874,10 +819,7 @@ export default [
|
||||
<Number
|
||||
id={state.inputTimeoutId}
|
||||
onChange={effects.setTimeout}
|
||||
value={defined(
|
||||
state.timeout,
|
||||
jobTimeout && jobTimeout / 1e3
|
||||
)}
|
||||
value={timeout && timeout / 1e3}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
@ -887,12 +829,8 @@ export default [
|
||||
<Icon icon='info' />
|
||||
</Tooltip>{' '}
|
||||
<input
|
||||
checked={defined(
|
||||
state.offlineSnapshot,
|
||||
jobOfflineSnapshot
|
||||
)}
|
||||
name='offlineSnapshot'
|
||||
onChange={effects.setCheckboxValue}
|
||||
checked={offlineSnapshot}
|
||||
onChange={effects.setOfflineSnapshot}
|
||||
type='checkbox'
|
||||
/>
|
||||
</label>
|
||||
|
@ -47,17 +47,13 @@ export default [
|
||||
level: 'danger',
|
||||
},
|
||||
],
|
||||
rowTransform: ({ settings }) => schedule => {
|
||||
const { exportRetention, copyRetention, snapshotRetention } =
|
||||
settings[schedule.id] || {}
|
||||
|
||||
return {
|
||||
...schedule,
|
||||
exportRetention,
|
||||
copyRetention,
|
||||
snapshotRetention,
|
||||
}
|
||||
},
|
||||
rowTransform: ({
|
||||
propSettings,
|
||||
settings = propSettings,
|
||||
}) => schedule => ({
|
||||
...schedule,
|
||||
...settings.get(schedule.id),
|
||||
}),
|
||||
schedulesColumns: (state, { effects: { toggleScheduleState } }) => {
|
||||
const columns = [
|
||||
{
|
||||
|
@ -5869,9 +5869,9 @@ ignore@^3.3.3, ignore@^3.3.5, ignore@^3.3.6:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
|
||||
immutable@^3.8.2:
|
||||
version "3.8.2"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||
immutable@^4.0.0-rc.9:
|
||||
version "4.0.0-rc.9"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.9.tgz#1e6e0094e649013ec3742d2b5aeeca5eeda4f0bf"
|
||||
|
||||
import-local@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user