feat(xo-web/tasks): display linked objects (#5267)

Fixes #4275
This commit is contained in:
Rajaa.BARHTAOUI 2020-09-29 15:13:56 +02:00 committed by GitHub
parent d1db616d1e
commit 66ba05dcd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 11 deletions

View File

@ -13,6 +13,7 @@
- [Groups] Ability to delete multiple groups at once (PR [#5264](https://github.com/vatesfr/xen-orchestra/pull/5264))
- [Backup/logs] Log's tasks pagination [#4406](https://github.com/vatesfr/xen-orchestra/issues/4406) (PR [#5209](https://github.com/vatesfr/xen-orchestra/pull/5209))
- [Backup logs] Ability to filter by VM/pool name [#4406](https://github.com/vatesfr/xen-orchestra/issues/4406) (PR [#5208](https://github.com/vatesfr/xen-orchestra/pull/5208))
- [Tasks] Show XO objects linked to pending/finished tasks [#4275](https://github.com/vatesfr/xen-orchestra/issues/4275) (PR [#5267](https://github.com/vatesfr/xen-orchestra/pull/5267))
### Bug fixes
@ -44,5 +45,6 @@
- @xen-orchestra/fs patch
- xo-vmdk-to-vhd patch
- xo-server minor
- xo-web minor
- xo-server patch

View File

@ -117,6 +117,7 @@ const TRANSFORMS = {
pool(obj) {
const cpuInfo = obj.cpu_info
return {
current_operations: obj.current_operations,
default_SR: link(obj, 'default_SR'),
HA_enabled: Boolean(obj.ha_enabled),
master: link(obj, 'master'),
@ -525,6 +526,7 @@ const TRANSFORMS = {
physical_usage: +obj.physical_utilisation,
allocationStrategy: ALLOCATION_BY_TYPE[srType],
current_operations: obj.current_operations,
name_description: obj.name_description,
name_label: obj.name_label,
size: +obj.physical_size,
@ -609,6 +611,7 @@ const TRANSFORMS = {
tags: obj.tags,
usage: +obj.physical_utilisation,
VDI_type: obj.type,
current_operations: obj.current_operations,
$SR: link(obj, 'SR'),
$VBDs: link(obj, 'VBDs'),
@ -682,6 +685,7 @@ const TRANSFORMS = {
return {
automatic: obj.other_config?.automatic === 'true',
bridge: obj.bridge,
current_operations: obj.current_operations,
defaultIsLocked: obj.default_locking_mode === 'disabled',
MTU: +obj.MTU,
name_description: obj.name_description,
@ -718,6 +722,7 @@ const TRANSFORMS = {
progress: +obj.progress,
result: obj.result,
status: obj.status,
xapiRef: obj.$ref,
$host: link(obj, 'resident_on'),
}

View File

@ -1546,6 +1546,7 @@ const messages = {
destroyTask: 'Destroy',
cancelTasks: 'Cancel selected tasks',
destroyTasks: 'Destroy selected tasks',
objects: 'Objects',
pool: 'Pool',
task: 'Task',
progress: 'Progress',

View File

@ -1,9 +1,11 @@
import _, { messages } from 'intl'
import Collapse from 'collapse'
import Component from 'base-component'
import defined from '@xen-orchestra/defined'
import Icon from 'icon'
import Link from 'link'
import React from 'react'
import renderXoItem, { Pool } from 'render-xo-item'
import SortedTable from 'sorted-table'
import { FormattedDate, injectIntl } from 'react-intl'
import { SelectPool } from 'select-objects'
@ -13,12 +15,14 @@ import {
differenceBy,
flatMap,
flatten,
forOwn,
groupBy,
isEmpty,
keys,
map,
some,
toArray,
} from 'lodash'
import { Pool } from 'render-xo-item'
import {
createFilter,
createGetObject,
@ -41,6 +45,16 @@ const HEADER = (
</Container>
)
const Ul = props => <ul {...props} style={{ listStyleType: 'none' }} />
const Li = props => (
<li
{...props}
style={{
whiteSpace: 'nowrap',
}}
/>
)
const TASK_ITEM_STYLE = {
// Remove all margin, otherwise it breaks vertical alignment.
margin: 0,
@ -70,6 +84,33 @@ export class TaskItem extends Component {
}
}
const taskObjectsRenderer = ({ objects }) => (
<Ul>
{map(objects, obj => {
const { id, type } = obj
return type === 'VDI' || type === 'network' ? (
<Li key={id}>{renderXoItem(obj)}</Li>
) : (
<Li key={id}>
<Link to={`/${type}s/${id}`}>{renderXoItem(obj)}</Link>
</Li>
)
})}
</Ul>
)
const COMMON = [
{
component: TaskItem,
name: _('task'),
sortCriteria: 'name_label',
},
{
itemRenderer: taskObjectsRenderer,
name: _('objects'),
},
]
const COLUMNS = [
{
default: true,
@ -80,11 +121,7 @@ const COLUMNS = [
return pool !== undefined && pool.name_label
},
},
{
component: TaskItem,
name: _('task'),
sortCriteria: 'name_label',
},
...COMMON,
{
itemRenderer: task => (
<progress
@ -104,10 +141,7 @@ const FINISHED_TASKS_COLUMNS = [
itemRenderer: ({ $poolId }) => <Pool id={$poolId} link />,
name: _('pool'),
},
{
component: TaskItem,
name: _('task'),
},
...COMMON,
{
default: true,
itemRenderer: task => (
@ -168,7 +202,54 @@ const GROUPED_ACTIONS = [
const getNPendingTasks = getPendingTasks.count()
const getPendingTasksByPool = getPendingTasks.sort().groupBy('$pool')
const predicate = obj => !isEmpty(obj.current_operations)
const getLinkedObjectsByTaskRefOrId = createSelector(
createGetObjectsOfType('pool').filter([predicate]),
createGetObjectsOfType('host').filter([predicate]),
createGetObjectsOfType('SR').filter([predicate]),
createGetObjectsOfType('VDI').filter([predicate]),
createGetObjectsOfType('VM').filter([predicate]),
createGetObjectsOfType('network').filter([predicate]),
(pools, hosts, srs, vdis, vms, networks) => {
const linkedObjectsByTaskRefOrId = {}
const resolveLinkedObjects = obj => {
Object.keys(obj.current_operations).forEach(task => {
if (linkedObjectsByTaskRefOrId[task] === undefined) {
linkedObjectsByTaskRefOrId[task] = []
}
linkedObjectsByTaskRefOrId[task].push(obj)
})
}
forOwn(pools, resolveLinkedObjects)
forOwn(hosts, resolveLinkedObjects)
forOwn(srs, resolveLinkedObjects)
forOwn(vdis, resolveLinkedObjects)
forOwn(vms, resolveLinkedObjects)
forOwn(networks, resolveLinkedObjects)
return linkedObjectsByTaskRefOrId
}
)
const getPendingTasksByPool = createSelector(
getPendingTasks,
getLinkedObjectsByTaskRefOrId,
(tasks, linkedObjectsByTaskRefOrId) =>
groupBy(
map(tasks, task => ({
...task,
objects: [
...defined(linkedObjectsByTaskRefOrId[task.xapiRef], []),
// for VMs, the current_operations prop is
// { taskId → operation } map instead of { taskRef → operation } map
...defined(linkedObjectsByTaskRefOrId[task.id], []),
],
})),
'$pool'
)
)
const getPools = createGetObjectsOfType('pool').pick(
createSelector(getPendingTasksByPool, keys)