feat(xo-web/backup/overview): add link from backup job/schedule to corresponding logs (#5260)
Fixes #4564
This commit is contained in:
parent
a99086b6bd
commit
009a0c5703
@ -10,6 +10,7 @@
|
|||||||
- [Host/Advanced] Add the field `IOMMU` if it is defined (PR [#5294](https://github.com/vatesfr/xen-orchestra/pull/5294))
|
- [Host/Advanced] Add the field `IOMMU` if it is defined (PR [#5294](https://github.com/vatesfr/xen-orchestra/pull/5294))
|
||||||
- [Backup logs/report] Hide merge task when no merge is done (PR [#5263](https://github.com/vatesfr/xen-orchestra/pull/5263))
|
- [Backup logs/report] Hide merge task when no merge is done (PR [#5263](https://github.com/vatesfr/xen-orchestra/pull/5263))
|
||||||
- [New backup] Enable created schedules by default (PR [#5280](https://github.com/vatesfr/xen-orchestra/pull/5280))
|
- [New backup] Enable created schedules by default (PR [#5280](https://github.com/vatesfr/xen-orchestra/pull/5280))
|
||||||
|
- [Backup/overview] Link backup jobs/schedules to their corresponding logs [#4564](https://github.com/vatesfr/xen-orchestra/issues/4564) (PR [#5260](https://github.com/vatesfr/xen-orchestra/pull/5260))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
@ -2104,6 +2104,7 @@ const messages = {
|
|||||||
backupForceRestartFailedVms: "Force restart failed VMs' backup",
|
backupForceRestartFailedVms: "Force restart failed VMs' backup",
|
||||||
clickForMoreInformation: 'Click for more information',
|
clickForMoreInformation: 'Click for more information',
|
||||||
goToThisJob: 'Click to go to this job',
|
goToThisJob: 'Click to go to this job',
|
||||||
|
goToCorrespondingLogs: 'Click to see corresponding logs',
|
||||||
|
|
||||||
// ----- IPs ------
|
// ----- IPs ------
|
||||||
ipPoolName: 'Name',
|
ipPoolName: 'Name',
|
||||||
|
@ -27,6 +27,7 @@ const Overview = decorate([
|
|||||||
provideState({
|
provideState({
|
||||||
initialState: () => ({
|
initialState: () => ({
|
||||||
scrollIntoJobs: undefined,
|
scrollIntoJobs: undefined,
|
||||||
|
scrollIntoLogs: undefined,
|
||||||
}),
|
}),
|
||||||
effects: {
|
effects: {
|
||||||
handleJobsRef(_, ref) {
|
handleJobsRef(_, ref) {
|
||||||
@ -34,6 +35,11 @@ const Overview = decorate([
|
|||||||
this.state.scrollIntoJobs = ref.scrollIntoView.bind(ref)
|
this.state.scrollIntoJobs = ref.scrollIntoView.bind(ref)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleLogsRef(_, ref) {
|
||||||
|
if (ref !== null) {
|
||||||
|
this.state.scrollIntoLogs = ref.scrollIntoView.bind(ref)
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
haveLegacyBackups: (_, { legacyJobs }) =>
|
haveLegacyBackups: (_, { legacyJobs }) =>
|
||||||
@ -41,7 +47,10 @@ const Overview = decorate([
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
injectState,
|
injectState,
|
||||||
({ effects, state: { haveLegacyBackups, scrollIntoJobs } }) => (
|
({
|
||||||
|
effects,
|
||||||
|
state: { haveLegacyBackups, scrollIntoJobs, scrollIntoLogs },
|
||||||
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
{haveLegacyBackups && <LegacyOverview />}
|
{haveLegacyBackups && <LegacyOverview />}
|
||||||
<div className='mt-2 mb-1'>
|
<div className='mt-2 mb-1'>
|
||||||
@ -52,11 +61,13 @@ const Overview = decorate([
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBlock>
|
<CardBlock>
|
||||||
<div ref={effects.handleJobsRef}>
|
<div ref={effects.handleJobsRef}>
|
||||||
<JobsTable />
|
<JobsTable scrollIntoLogs={scrollIntoLogs} />
|
||||||
</div>
|
</div>
|
||||||
</CardBlock>
|
</CardBlock>
|
||||||
</Card>
|
</Card>
|
||||||
<LogsTable scrollIntoJobs={scrollIntoJobs} />
|
<div ref={effects.handleLogsRef}>
|
||||||
|
<LogsTable scrollIntoJobs={scrollIntoJobs} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -18,7 +18,9 @@ import { createFilter, createGetObjectsOfType, createSelector } from 'selectors'
|
|||||||
import { createPredicate } from 'value-matcher'
|
import { createPredicate } from 'value-matcher'
|
||||||
import { get } from '@xen-orchestra/defined'
|
import { get } from '@xen-orchestra/defined'
|
||||||
import { groupBy, isEmpty, map, some } from 'lodash'
|
import { groupBy, isEmpty, map, some } from 'lodash'
|
||||||
|
import { injectState, provideState } from 'reaclette'
|
||||||
import { Proxy } from 'render-xo-item'
|
import { Proxy } from 'render-xo-item'
|
||||||
|
import { withRouter } from 'react-router'
|
||||||
import {
|
import {
|
||||||
cancelJob,
|
cancelJob,
|
||||||
deleteBackupJobs,
|
deleteBackupJobs,
|
||||||
@ -110,6 +112,49 @@ const _runBackupJob = ({ id, name, nVms, schedule, type }) =>
|
|||||||
: runMetadataBackupJob({ id, schedule })
|
: runMetadataBackupJob({ id, schedule })
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const CURSOR_POINTER_STYLE = { cursor: 'pointer' }
|
||||||
|
const GoToLogs = decorate([
|
||||||
|
withRouter,
|
||||||
|
provideState({
|
||||||
|
effects: {
|
||||||
|
goTo() {
|
||||||
|
const {
|
||||||
|
jobId,
|
||||||
|
location,
|
||||||
|
router,
|
||||||
|
scheduleId,
|
||||||
|
scrollIntoLogs,
|
||||||
|
} = this.props
|
||||||
|
router.replace({
|
||||||
|
...location,
|
||||||
|
query: {
|
||||||
|
...location.query,
|
||||||
|
s_logs:
|
||||||
|
jobId !== undefined
|
||||||
|
? `jobId:${jobId}`
|
||||||
|
: `scheduleId:${scheduleId}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
scrollIntoLogs()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
injectState,
|
||||||
|
({ effects, children }) => (
|
||||||
|
<Tooltip content={_('goToCorrespondingLogs')}>
|
||||||
|
<span onClick={effects.goTo} style={CURSOR_POINTER_STYLE}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
GoToLogs.propTypes = {
|
||||||
|
jobId: PropTypes.string,
|
||||||
|
scheduleId: PropTypes.string,
|
||||||
|
scrollIntoLogs: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
const SchedulePreviewBody = decorate([
|
const SchedulePreviewBody = decorate([
|
||||||
addSubscriptions(({ schedule }) => ({
|
addSubscriptions(({ schedule }) => ({
|
||||||
lastRunLog: cb =>
|
lastRunLog: cb =>
|
||||||
@ -138,12 +183,14 @@ const SchedulePreviewBody = decorate([
|
|||||||
.filter(createSelector((_, props) => props.job.vms, createPredicate))
|
.filter(createSelector((_, props) => props.job.vms, createPredicate))
|
||||||
.count(),
|
.count(),
|
||||||
})),
|
})),
|
||||||
({ job, schedule, lastRunLog, nVms }) => (
|
({ job, schedule, scrollIntoLogs, lastRunLog, nVms }) => (
|
||||||
<Ul>
|
<Ul>
|
||||||
<Li>
|
<Li>
|
||||||
{schedule.name
|
<GoToLogs scheduleId={schedule.id} scrollIntoLogs={scrollIntoLogs}>
|
||||||
? _.keyValue(_('scheduleName'), schedule.name)
|
{schedule.name
|
||||||
: _.keyValue(_('scheduleCron'), schedule.cron)}{' '}
|
? _.keyValue(_('scheduleName'), schedule.name)
|
||||||
|
: _.keyValue(_('scheduleCron'), schedule.cron)}
|
||||||
|
</GoToLogs>{' '}
|
||||||
<Tooltip content={_('scheduleCopyId', { id: schedule.id.slice(4, 8) })}>
|
<Tooltip content={_('scheduleCopyId', { id: schedule.id.slice(4, 8) })}>
|
||||||
<CopyToClipboard text={schedule.id}>
|
<CopyToClipboard text={schedule.id}>
|
||||||
<Button size='small'>
|
<Button size='small'>
|
||||||
@ -226,9 +273,11 @@ class JobsTable extends React.Component {
|
|||||||
static tableProps = {
|
static tableProps = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
itemRenderer: ({ id }) => (
|
itemRenderer: ({ id }, { scrollIntoLogs }) => (
|
||||||
<Copiable data={id} tagName='p'>
|
<Copiable data={id} tagName='p'>
|
||||||
{id.slice(4, 8)}
|
<GoToLogs jobId={id} scrollIntoLogs={scrollIntoLogs}>
|
||||||
|
{id.slice(4, 8)}
|
||||||
|
</GoToLogs>
|
||||||
</Copiable>
|
</Copiable>
|
||||||
),
|
),
|
||||||
name: _('jobId'),
|
name: _('jobId'),
|
||||||
@ -250,7 +299,7 @@ class JobsTable extends React.Component {
|
|||||||
name: _('jobModes'),
|
name: _('jobModes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
itemRenderer: (job, { schedulesByJob }) =>
|
itemRenderer: (job, { schedulesByJob, scrollIntoLogs }) =>
|
||||||
map(
|
map(
|
||||||
get(() => schedulesByJob[job.id]),
|
get(() => schedulesByJob[job.id]),
|
||||||
schedule => (
|
schedule => (
|
||||||
@ -258,6 +307,7 @@ class JobsTable extends React.Component {
|
|||||||
job={job}
|
job={job}
|
||||||
key={schedule.id}
|
key={schedule.id}
|
||||||
schedule={schedule}
|
schedule={schedule}
|
||||||
|
scrollIntoLogs={scrollIntoLogs}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -399,6 +449,7 @@ class JobsTable extends React.Component {
|
|||||||
data-goToNewTab={this._goToNewTab}
|
data-goToNewTab={this._goToNewTab}
|
||||||
data-main={this.props.main}
|
data-main={this.props.main}
|
||||||
data-schedulesByJob={this.props.schedulesByJob}
|
data-schedulesByJob={this.props.schedulesByJob}
|
||||||
|
data-scrollIntoLogs={this.props.scrollIntoLogs}
|
||||||
stateUrlParam='s'
|
stateUrlParam='s'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user