Skip to content

Commit

Permalink
Merge branch 'master' into fix-private-data-in-api-call
Browse files Browse the repository at this point in the history
  • Loading branch information
MathieuRA authored Sep 27, 2024
2 parents 6a7ab2a + cdfe0b0 commit 19069f9
Show file tree
Hide file tree
Showing 18 changed files with 506 additions and 37 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
- `rest get --output $file` now displays progress information during download
- `rest post` and `rest put` now accept `--input $file` to upload a file and display progress information
- [Backup] Detect invalid VDI exports that are incorrectly reported as successful by XAPI
- [Backup/HealthCheck] Improve error messages on health check timeout (PR [#8016](https://github.com/vatesfr/xen-orchestra/pull/8016))
- [Backup] Backup job sequences: configure lists of backup jobs to run in order one after the other (PRs [#7985](https://github.com/vatesfr/xen-orchestra/pull/7985), [#8014](https://github.com/vatesfr/xen-orchestra/pull/8014))
- [Pool/Network] Display the bond mode of a network [#7802](https://github.com/vatesfr/xen-orchestra/issues/7802) (PR [#8010](https://github.com/vatesfr/xen-orchestra/pull/8010))

### Bug fixes

> Users must be able to say: “I had this issue, happy to know it's fixed”
- [REST API] Fix broken _Rolling Pool Update_ pool action [Forum#82867](https://xcp-ng.org/forum/post/82867)
- [Logs] Fix private data in API call: password now obfuscated (PR [#8019](https://github.com/vatesfr/xen-orchestra/pull/8019))

### Packages to release
Expand All @@ -50,6 +52,6 @@
- xen-api minor
- xo-cli minor
- xo-server minor
- xo-web patch
- xo-web minor

<!--packages-end-->
24 changes: 24 additions & 0 deletions packages/xo-server/src/api/schedule.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// FIXME so far, no acls for schedules

import { Task } from '@xen-orchestra/mixins/Tasks.mjs'

export async function getAll() {
return /* await */ this.getAllSchedules()
}
Expand Down Expand Up @@ -64,3 +66,25 @@ delete_.params = {
}

export { delete_ as delete }

export async function runSequence({ schedules }) {
const t = this.tasks.create({ type: 'xo:schedule:sequence', name: 'Schedule sequence' })
await t.run(async () => {
const nb = schedules.length
const signal = Task.abortSignal
for (let i = 0; i < nb; i++) {
signal.throwIfAborted()
Task.set('progress', Math.round((i * 100) / nb))
const idSchedule = schedules[i]
// we can't auto resolve array parameters, we have to resolve them by hand
const schedule = await this.getSchedule(idSchedule)
const job = await this.getJob(schedule.jobId)
await this.runJob(job, schedule)
}
})
}
runSequence.permission = 'admin'
runSequence.description = 'Run a sequence of schedules, one after the other'
runSequence.params = {
schedules: { type: 'array', items: { type: 'string' } },
}
10 changes: 10 additions & 0 deletions packages/xo-server/src/xapi-object-to-xo.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,16 @@ const TRANSFORMS = {
VUSBs: link(obj, 'VUSBs'),
}
},

// -----------------------------------------------------------------

bond(obj) {
return {
type: 'bond',
master: link(obj, 'master'),
mode: obj.mode,
}
},
}

// ===================================================================
Expand Down
20 changes: 16 additions & 4 deletions packages/xo-server/src/xo-mixins/api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -316,18 +316,30 @@ export default class Api {
throw new MethodNotFound(name)
}

const apiContext = { __proto__: null, connection }

let user
const userId = connection.get('user_id', undefined)
if (userId !== undefined) {
const user = await this._app.getUser(userId)
user = await this._app.getUser(userId)
}

return this.runWithApiContext(user, () => {
this.apiContext.connection = connection

return this.#callApiMethod(name, method, params)
})
}

async runWithApiContext(user, fn) {
const apiContext = { __proto__: null }

if (user !== undefined) {
apiContext.user = user
apiContext.permission = user.permission
} else {
apiContext.permission = 'none'
}

return this.#apiContext.run(apiContext, () => this.#callApiMethod(name, method, params))
return this.#apiContext.run(apiContext, fn)
}

async #callApiMethod(name, method, params) {
Expand Down
4 changes: 2 additions & 2 deletions packages/xo-server/src/xo-mixins/jobs/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export default class Jobs {
}

@decorateWith(defer)
async _runJob($defer, job, schedule, data_) {
async runJob($defer, job, schedule, data_) {
const logger = this._logger
const { id, type } = job

Expand Down Expand Up @@ -316,7 +316,7 @@ export default class Jobs {
const jobs = await Promise.all(idSequence.map(id => this.getJob(id)))

for (const job of jobs) {
await this._runJob(job, schedule, data)
await this.runJob(job, schedule, data)
}
}
}
5 changes: 2 additions & 3 deletions packages/xo-server/src/xo-mixins/rest-api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,7 @@ export default class RestApi {
app.authenticateUser({ token: cookies.authenticationToken ?? cookies.token }, { ip }).then(
({ user }) => {
if (user.permission === 'admin') {
req.user = user
return next()
return app.runWithApiContext(user, next)
}

res.sendStatus(401)
Expand Down Expand Up @@ -658,7 +657,7 @@ export default class RestApi {
params.affinityHost = affinity
params.installRepository = install?.repository

const vm = await $xapi.createVm(template, params, undefined, req.user.id)
const vm = await $xapi.createVm(template, params, undefined, app.apiContext.user.id)
$defer.onFailure.call($xapi, 'VM_destroy', vm.$ref)

if (boot) {
Expand Down
6 changes: 6 additions & 0 deletions packages/xo-web/src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ const messages = {
xcpNg: 'XCP-ng',
noFileSelected: 'No file selected',
nRetriesVmBackupFailures: 'Number of retries if VM backup fails',
sequence: 'Sequence',
sequences: 'Sequences',

// ----- Modals -----
alertOk: 'OK',
Expand Down Expand Up @@ -531,7 +533,9 @@ const messages = {
scheduleAdd: 'Add a schedule',
scheduleDelete: 'Delete',
scheduleRun: 'Run schedule',
scheduleSequence: 'Schedule sequence',
unnamedSchedule: 'Unnamed schedule',
unnamedJob: 'Unnamed Job',
deleteSelectedSchedules: 'Delete selected schedules',
noScheduledJobs: 'No scheduled jobs.',
legacySnapshotsLink: 'You can delete all your legacy backup snapshots.',
Expand Down Expand Up @@ -969,6 +973,7 @@ const messages = {
noHost: 'No hosts',
memoryLeftTooltip: '{used}% used ({free} free)',
// ----- Pool network tab -----
bondMode: 'Bond Mode',
pif: 'PIF',
poolNetworkAutomatic: 'Automatic',
poolNetworkNameLabel: 'Name',
Expand Down Expand Up @@ -1670,6 +1675,7 @@ const messages = {
missingVm: 'Missing VM',
missingVmInJob: 'This VM does not belong to this job',
missingSchedule: 'Missing schedule',
unknownSchedule: 'Unknown schedule',
noDetachedBackups: 'No backups',
noDuplicatedMacAddresses: 'No duplicated MAC addresses',
reason: 'Reason',
Expand Down
82 changes: 69 additions & 13 deletions packages/xo-web/src/common/render-xo-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CopyToClipboard from 'react-copy-to-clipboard'
import PropTypes from 'prop-types'
import React from 'react'
import { get } from '@xen-orchestra/defined'
import { injectState, provideState } from 'reaclette'
import find from 'lodash/find.js'
import isEmpty from 'lodash/isEmpty.js'

Expand All @@ -13,7 +14,16 @@ import Link from './link'
import Tooltip from './tooltip'
import { addSubscriptions, connectStore, formatSize, NumericDate, ShortDate } from './utils'
import { createGetObject, createSelector } from './selectors'
import { isSrWritable, subscribeBackupNgJobs, subscribeProxies, subscribeRemotes, subscribeUsers } from './xo'
import {
isSrWritable,
subscribeBackupNgJobs,
subscribeMetadataBackupJobs,
subscribeProxies,
subscribeRemotes,
subscribeSchedules,
subscribeUsers,
subscribeMirrorBackupJobs,
} from './xo'

// ===================================================================

Expand Down Expand Up @@ -493,6 +503,62 @@ BackupJob.defaultProps = {

// ===================================================================

export const Schedule = decorate([
addSubscriptions(({ id }) => ({
schedule: cb => subscribeSchedules(schedules => cb(schedules.find(schedule => schedule.id === id))),
backupJobs: subscribeBackupNgJobs,
metadataBackupJobs: subscribeMetadataBackupJobs,
mirrorBackupJobs: subscribeMirrorBackupJobs,
})),
provideState({
initialState: () => ({}),
computed: {
job: (_, { backupJobs = [], metadataBackupJobs = [], mirrorBackupJobs = [], schedule }) =>
schedule && [...backupJobs, ...metadataBackupJobs, ...mirrorBackupJobs].find(job => job.id === schedule.jobId),
},
}),
injectState,
({ id, schedule, showJob, showState, state: { job } }) => {
if (schedule === undefined) {
return unknowItem(id, 'schedule')
}

const isEnabled = schedule.enabled
const scheduleName = schedule.name.trim()
const jobName = job?.name.trim()

return (
<span>
{showState && (
<span className={`mr-1 tag tag-${isEnabled ? 'success' : 'danger'}`}>
{isEnabled ? _('stateEnabled') : _('stateDisabled')}
</span>
)}
<span>{scheduleName === '' ? <em>{_('unnamedSchedule')}</em> : scheduleName}</span>
{showJob && (
<span>
{' '}
({job ? jobName === '' ? <em>{_('unnamedJob')}</em> : jobName : unknowItem(schedule.jobId, 'job')})
</span>
)}
</span>
)
},
])

Schedule.propTypes = {
id: PropTypes.string.isRequired,
showState: PropTypes.bool,
showJob: PropTypes.bool,
}

Schedule.defaultProps = {
showState: true,
showJob: true,
}

// ===================================================================

export const Vgpu = connectStore(() => ({
vgpuType: createGetObject((_, props) => props.vgpu.vgpuType),
}))(({ vgpu, vgpuType }) => (
Expand Down Expand Up @@ -684,18 +750,8 @@ const xoItemToRender = {

PCI: props => <Pci {...props} self />,

schedule: schedule => {
const isEnabled = schedule.enabled
const scheduleName = schedule.name.trim()
return (
<span>
<span className={`mr-1 tag tag-${isEnabled ? 'success' : 'danger'}`}>
{isEnabled ? _('stateEnabled') : _('stateDisabled')}
</span>
<span>{scheduleName === '' ? <em>{_('unnamedSchedule')}</em> : scheduleName}</span>
</span>
)
},
schedule: props => <Schedule {...props} />,

job: job => <spans>{job.name}</spans>,
}

Expand Down
1 change: 1 addition & 0 deletions packages/xo-web/src/common/select-objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class GenericSelect extends React.Component {
: undefined,
memoryFree: option.xoItem.type === 'host' || undefined,
showNetwork: true,
...(this.props.optionProps ?? {}),
})}
</span>
)
Expand Down
11 changes: 8 additions & 3 deletions packages/xo-web/src/common/sorted-table/pifs-column.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import map from 'lodash/map'
import React, { Component } from 'react'
import Tooltip from 'tooltip'
import { connectStore } from 'utils'
import { createGetObject, createSelector } from 'selectors'
import { createGetObject, createGetObjectsOfType, createSelector } from 'selectors'
import { connectPif, disconnectPif } from 'xo'

@connectStore(() => {
Expand All @@ -19,18 +19,22 @@ import { connectPif, disconnectPif } from 'xo'
pif => pif?.attached && !pif?.isBondMaster && (pif?.management || pif?.disallowUnplug)
)

return { host, pif, disableUnplug }
const bonds = createGetObjectsOfType('bond')
const bond = createSelector(pif, bonds, (pif, bonds) => Object.values(bonds).find(bond => bond.master === pif.id))

return { host, pif, disableUnplug, bond }
})
class PifItem extends Component {
render() {
const { pif, host, disableUnplug } = this.props
const { pif, host, disableUnplug, bond } = this.props

return (
<tr>
<td>{pif?.device ?? _('unknown')}</td>
<td>{host?.name_label ?? _('unknown')}</td>
<td>{pif?.ip ?? _('unknown')}</td>
<td>{pif?.mac ?? _('unknown')}</td>
<td>{bond?.mode ?? '-'}</td>
<td>
{pif?.carrier === undefined ? (
<span className='tag tag-warning'>{_('unknown')}</span>
Expand Down Expand Up @@ -78,6 +82,7 @@ export default class PifsColumn extends BaseComponent {
<th>{_('homeTypeHost')}</th>
<th>{_('pifAddressLabel')}</th>
<th>{_('pifMacLabel')}</th>
<th>{_('bondMode')}</th>
<th>{_('pifStatusLabel')}</th>
<th />
</tr>
Expand Down
4 changes: 4 additions & 0 deletions packages/xo-web/src/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,10 @@
@extend .fa;
@extend .fa-eye;
}
&-sequence {
@extend .fa;
@extend .fa-list-ol;
}
&-new {
@extend .fa;
@extend .fa-plus;
Expand Down
Loading

0 comments on commit 19069f9

Please sign in to comment.