feat(xo-web/backup-ng/logs): copy full log and report a failed job (#3110)

Fixes #3100
This commit is contained in:
badrAZ 2018-06-28 10:57:35 +02:00 committed by Julien Fontanet
parent 358e1441cc
commit 40568cd61f
5 changed files with 134 additions and 96 deletions

View File

@ -21,6 +21,7 @@
- Add legacy backups snapshots to backup/health [#3082](https://github.com/vatesfr/xen-orchestra/issues/3082) (PR [#3111](https://github.com/vatesfr/xen-orchestra/pull/3111))
- [Backup NG logs] Add the job's name to the modal's title [#2711](https://github.com/vatesfr/xen-orchestra/issues/2711) (PR [#3115](https://github.com/vatesfr/xen-orchestra/pull/3115))
- Adding a XCP-ng host to a XS pool now fails fast [#3061](https://github.com/vatesfr/xen-orchestra/issues/3061) (PR [#3118](https://github.com/vatesfr/xen-orchestra/pull/3118))
- [Backup NG logs] Ability to report a failed job and copy its log to the clipboard [#3100](https://github.com/vatesfr/xen-orchestra/issues/3100) (PR [#3110](https://github.com/vatesfr/xen-orchestra/pull/3110))
### Bugs

View File

@ -0,0 +1,51 @@
import _ from 'intl'
import React from 'react'
import ActionButton from './action-button'
import ActionRowButton from './action-row-button'
import propTypes from './prop-types-decorator'
export const CAN_REPORT_BUG = process.env.XOA_PLAN > 1
const reportBug = ({ formatMessage, message, title }) => {
const encodedTitle = encodeURIComponent(title)
const encodedMessage = encodeURIComponent(
formatMessage !== undefined ? formatMessage(message) : message
)
window.open(
process.env.XOA_PLAN < 5
? `https://xen-orchestra.com/#!/member/support?title=${encodedTitle}&message=${encodedMessage}`
: `https://github.com/vatesfr/xen-orchestra/issues/new?title=${encodedTitle}&body=${encodedMessage}`
)
}
const ReportBugButton = ({
formatMessage,
message,
rowButton,
title,
...props
}) => {
const Button = rowButton ? ActionRowButton : ActionButton
return (
<Button
{...props}
data-formatMessage={formatMessage}
data-message={message}
data-title={title}
handler={reportBug}
icon='bug'
tooltip={_('reportBug')}
/>
)
}
propTypes(ReportBugButton)({
formatMessage: propTypes.func,
message: propTypes.string.isRequired,
rowButton: propTypes.bool,
title: propTypes.string.isRequired,
})
export default ReportBugButton

View File

@ -1,9 +1,14 @@
import _, { FormattedDuration } from 'intl'
import addSubscriptions from 'add-subscriptions'
import Button from 'button'
import ButtonGroup from 'button-group'
import CopyToClipboard from 'react-copy-to-clipboard'
import Icon from 'icon'
import NoObjects from 'no-objects'
import React from 'react'
import ReportBugButton, { CAN_REPORT_BUG } from 'report-bug-button'
import SortedTable from 'sorted-table'
import Tooltip from 'tooltip'
import { alert } from 'modal'
import { Card, CardHeader, CardBlock } from 'card'
import { keyBy } from 'lodash'
@ -102,16 +107,37 @@ const LOG_COLUMNS = [
},
]
const showTasks = (log, { jobs }) =>
const showTasks = (log, { jobs }) => {
const formattedLog = JSON.stringify(log, null, 2)
alert(
<span>
{get(() => jobs[log.jobId].name) || 'Job'} ({log.jobId.slice(4, 8)}){' '}
<span style={{ fontSize: '0.5em' }} className='text-muted'>
{log.id}
</span>
</span>{' '}
{log.status !== 'success' &&
log.status !== 'pending' && (
<ButtonGroup>
<Tooltip content={_('copyToClipboard')}>
<CopyToClipboard text={formattedLog}>
<Button size='small'>
<Icon icon='clipboard' />
</Button>
</CopyToClipboard>
</Tooltip>
{CAN_REPORT_BUG && (
<ReportBugButton
message={`\`\`\`json\n${formattedLog}\n\`\`\``}
size='small'
title='Backup job failed'
/>
)}
</ButtonGroup>
)}
</span>,
<LogAlertBody id={log.id} />
)
}
const LOG_INDIVIDUAL_ACTIONS = [
{

View File

@ -1,6 +1,5 @@
import _, { FormattedDuration } from 'intl'
import ActionButton from 'action-button'
import Copiable from 'copiable'
import Icon from 'icon'
import React from 'react'
import renderXoItem, { renderXoItemFromId } from 'render-xo-item'
@ -148,9 +147,7 @@ export default [
return (status === 'failure' || status === 'skipped') &&
result !== undefined ? (
<span className={status === 'skipped' ? 'text-info' : 'text-danger'}>
<Copiable tagName='p' data={JSON.stringify(result, null, 2)}>
<Icon icon='alarm' /> {result.message}
</Copiable>
<Icon icon='alarm' /> {result.message}
</span>
) : (
<div>
@ -258,41 +255,32 @@ export default [
/>
)}
<br />
{operationLog.status === 'failure' ? (
<Copiable
tagName='p'
data={JSON.stringify(
operationLog.result,
null,
2
)}
>
{_.keyValue(
{operationLog.status === 'failure'
? _.keyValue(
_('taskError'),
<span className='text-danger'>
{operationLog.result.message}
</span>
)
: operationLog.result.size > 0 && (
<div>
{_.keyValue(
_('operationSize'),
formatSize(
operationLog.result.size
)
)}
<br />
{_.keyValue(
_('operationSpeed'),
formatSpeed(
operationLog.result.size,
operationLog.end -
operationLog.start
)
)}
</div>
)}
</Copiable>
) : (
operationLog.result.size > 0 && (
<div>
{_.keyValue(
_('operationSize'),
formatSize(operationLog.result.size)
)}
<br />
{_.keyValue(
_('operationSpeed'),
formatSpeed(
operationLog.result.size,
operationLog.end -
operationLog.start
)
)}
</div>
)
)}
</div>
)}
</li>
@ -313,22 +301,12 @@ export default [
)}
<br />
{subTaskLog.status === 'failure' &&
subTaskLog.result !== undefined && (
<Copiable
tagName='p'
data={JSON.stringify(
subTaskLog.result,
null,
2
)}
>
{_.keyValue(
_('taskError'),
<span className='text-danger'>
{subTaskLog.result.message}
</span>
)}
</Copiable>
subTaskLog.result !== undefined &&
_.keyValue(
_('taskError'),
<span className='text-danger'>
{subTaskLog.result.message}
</span>
)}
</div>
)}
@ -362,25 +340,20 @@ export default [
</a>
</Tooltip>
) : (
<Copiable
tagName='p'
data={JSON.stringify(taskLog.result, null, 2)}
>
{_.keyValue(
taskLog.status === 'skipped'
? _('taskReason')
: _('taskError'),
<span
className={
taskLog.status === 'skipped'
? 'text-info'
: 'text-danger'
}
>
{taskLog.result.message}
</span>
)}
</Copiable>
_.keyValue(
taskLog.status === 'skipped'
? _('taskReason')
: _('taskError'),
<span
className={
taskLog.status === 'skipped'
? 'text-info'
: 'text-danger'
}
>
{taskLog.result.message}
</span>
)
)
) : (
<div>

View File

@ -8,6 +8,7 @@ import BaseComponent from 'base-component'
import ButtonGroup from 'button-group'
import Copiable from 'copiable'
import NoObjects from 'no-objects'
import ReportBugButton, { CAN_REPORT_BUG } from 'report-bug-button'
import SortedTable from 'sorted-table'
import styles from './index.css'
import TabButton from 'tab-button'
@ -16,27 +17,12 @@ import { alert, confirm } from 'modal'
import { createSelector } from 'selectors'
import { subscribeApiLogs, subscribeUsers, deleteApiLog } from 'xo'
const CAN_REPORT_BUG = process.env.XOA_PLAN > 1
const reportBug = log => {
const title = encodeURIComponent(`Error on ${log.data.method}`)
const message = encodeURIComponent(
`\`\`\`\n${log.data.method}\n${JSON.stringify(
log.data.params,
null,
2
)}\n${JSON.stringify(log.data.error, null, 2).replace(
/\\n/g,
'\n'
)}\n\`\`\``
)
window.open(
process.env.XOA_PLAN < 5
? `https://xen-orchestra.com/#!/member/support?title=${title}&message=${message}`
: `https://github.com/vatesfr/xen-orchestra/issues/new?title=${title}&body=${message}`
)
}
const formatMessage = data =>
`\`\`\`\n${data.method}\n${JSON.stringify(
data.params,
null,
2
)}\n${JSON.stringify(data.error, null, 2).replace(/\\n/g, '\n')}\n\`\`\``
const COLUMNS = [
{
@ -102,10 +88,11 @@ const COLUMNS = [
tooltip={_('logDelete')}
/>
{CAN_REPORT_BUG && (
<ActionRowButton
handler={() => reportBug(log)}
icon='bug'
tooltip={_('reportBug')}
<ReportBugButton
message={log.data}
formatMessage={formatMessage}
rowButton
title={`Error on ${log.data.method}`}
/>
)}
</ButtonGroup>