Compare commits
6 Commits
feat_retry
...
fix_xovmdk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9797e6aecf | ||
|
|
aefcce45ff | ||
|
|
367fb4d8a6 | ||
|
|
e54a0bfc80 | ||
|
|
9e5541703b | ||
|
|
039d5687c0 |
@@ -141,7 +141,6 @@ export class Task {
|
||||
}
|
||||
|
||||
#log(event, props) {
|
||||
console.log({taskId: this.#id,event})
|
||||
this.#onLog({
|
||||
...props,
|
||||
event,
|
||||
|
||||
@@ -88,89 +88,49 @@ export const VmsXapi = class VmsXapiBackupRunner extends Abstract {
|
||||
|
||||
const allSettings = this._job.settings
|
||||
const baseSettings = this._baseSettings
|
||||
|
||||
|
||||
const handleVm = vmUuid => {
|
||||
const taskStart = { name: 'backup VM', data: { type: 'VM', id: vmUuid } }
|
||||
|
||||
const task = new Task(taskStart)
|
||||
let nbRun = 0
|
||||
|
||||
console.log('runonce', vmUuid)
|
||||
// ensure all the eecution are run in the same task
|
||||
const runOnce = async ()=> {
|
||||
nbRun ++
|
||||
console.log('Will run backup ', vmUuid, nbRun)
|
||||
|
||||
return this._getRecord('VM', vmUuid).then(
|
||||
disposableVm => Disposable.use(disposableVm, async vm => {
|
||||
taskStart.data.name_label = vm.name_label
|
||||
const opts = {
|
||||
baseSettings,
|
||||
config,
|
||||
getSnapshotNameLabel,
|
||||
healthCheckSr,
|
||||
job,
|
||||
remoteAdapters,
|
||||
schedule,
|
||||
settings: { ...settings, ...allSettings[vm.uuid] },
|
||||
srs,
|
||||
throttleStream,
|
||||
vm,
|
||||
}
|
||||
let vmBackup
|
||||
if (job.mode === 'delta') {
|
||||
vmBackup = new IncrementalXapi(opts)
|
||||
} else {
|
||||
if (job.mode === 'full') {
|
||||
vmBackup = new FullXapi(opts)
|
||||
} else {
|
||||
throw new Error(`Job mode ${job.mode} not implemented`)
|
||||
}
|
||||
}
|
||||
|
||||
const res = await vmBackup.run()
|
||||
console.log(' backup run successfully ', vmUuid, res)
|
||||
return res
|
||||
}))
|
||||
}
|
||||
// ensure the same task is reused
|
||||
return task.wrapFn(runOnce)
|
||||
|
||||
|
||||
}
|
||||
|
||||
let toHandle = []
|
||||
console.log('prepare to run ')
|
||||
for(const vmUuid of vmIds){
|
||||
// prepare a collection of task to bound task to run
|
||||
toHandle.push( handleVm(vmUuid))
|
||||
}
|
||||
console.log({toHandle})
|
||||
|
||||
for(let i=0; i < 4 && toHandle.length >0; i++){
|
||||
console.log('RUN ', i)
|
||||
const currentRun = [...toHandle]
|
||||
toHandle = []
|
||||
await asyncMapSettled(currentRun, async fn=>{
|
||||
console.log('got fn ', fn)
|
||||
try{
|
||||
await fn()
|
||||
console.log('run done')
|
||||
}catch(error){
|
||||
console.log('will retry ')
|
||||
toHandle.push(fn)
|
||||
}
|
||||
})
|
||||
return this._getRecord('VM', vmUuid).then(
|
||||
disposableVm =>
|
||||
Disposable.use(disposableVm, vm => {
|
||||
taskStart.data.name_label = vm.name_label
|
||||
return runTask(taskStart, () => {
|
||||
const opts = {
|
||||
baseSettings,
|
||||
config,
|
||||
getSnapshotNameLabel,
|
||||
healthCheckSr,
|
||||
job,
|
||||
remoteAdapters,
|
||||
schedule,
|
||||
settings: { ...settings, ...allSettings[vm.uuid] },
|
||||
srs,
|
||||
throttleStream,
|
||||
vm,
|
||||
}
|
||||
let vmBackup
|
||||
if (job.mode === 'delta') {
|
||||
vmBackup = new IncrementalXapi(opts)
|
||||
} else {
|
||||
if (job.mode === 'full') {
|
||||
vmBackup = new FullXapi(opts)
|
||||
} else {
|
||||
throw new Error(`Job mode ${job.mode} not implemented`)
|
||||
}
|
||||
}
|
||||
return vmBackup.run()
|
||||
})
|
||||
}),
|
||||
error =>
|
||||
runTask(taskStart, () => {
|
||||
throw error
|
||||
})
|
||||
)
|
||||
}
|
||||
const { concurrency } = settings
|
||||
if(toHandle.length > 0){
|
||||
console.log('LAST RUN ')
|
||||
// last run will really fail this time
|
||||
await asyncMapSettled(toHandle, fn=>fn())
|
||||
|
||||
}
|
||||
|
||||
await asyncMapSettled(vmIds, concurrency === 0 ? handleVm : limitConcurrency(concurrency)(handleVm))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,11 +64,6 @@ export class FullRemoteWriter extends MixinRemoteWriter(AbstractFullWriter) {
|
||||
}
|
||||
|
||||
await Task.run({ name: 'transfer' }, async () => {
|
||||
console.log('run ')
|
||||
if(Math.random() < 0.8){
|
||||
throw new Error('NOPE')
|
||||
}
|
||||
console.log('OK ')
|
||||
await adapter.outputStream(dataFilename, stream, {
|
||||
maxStreamLength,
|
||||
streamLength,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
- [Plugins/audit] Don't log `tag.getAllConfigured` calls
|
||||
- [Remotes] Correctly clear error when the remote is tested with success
|
||||
- [Import/VMWare] Fix importing last snapshot (PR [#7370](https://github.com/vatesfr/xen-orchestra/pull/7370))
|
||||
- [Host/Reboot] Fix false positive warning when restarting an host after updates (PR [#7366](https://github.com/vatesfr/xen-orchestra/pull/7366))
|
||||
|
||||
### Packages to release
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semver from 'semver'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import assert from 'assert'
|
||||
import { format } from 'json-rpc-peer'
|
||||
@@ -136,13 +137,38 @@ export async function restart({
|
||||
const pool = this.getObject(host.$poolId, 'pool')
|
||||
const master = this.getObject(pool.master, 'host')
|
||||
const hostRebootRequired = host.rebootRequired
|
||||
if (hostRebootRequired && host.id !== master.id && host.version === master.version) {
|
||||
throw incorrectState({
|
||||
actual: hostRebootRequired,
|
||||
expected: false,
|
||||
object: master.id,
|
||||
property: 'rebootRequired',
|
||||
})
|
||||
|
||||
// we are currently in an host upgrade process
|
||||
if (hostRebootRequired && host.id !== master.id) {
|
||||
// this error is not ideal but it means that the pool master must be fully upgraded/rebooted before the current host can be rebooted.
|
||||
//
|
||||
// there is a single error for the 3 cases because the client must handle them the same way
|
||||
const throwError = () =>
|
||||
incorrectState({
|
||||
actual: hostRebootRequired,
|
||||
expected: false,
|
||||
object: master.id,
|
||||
property: 'rebootRequired',
|
||||
})
|
||||
|
||||
if (semver.lt(master.version, host.version)) {
|
||||
log.error(`master version (${master.version}) is older than the host version (${host.version})`, {
|
||||
masterId: master.id,
|
||||
hostId: host.id,
|
||||
})
|
||||
throwError()
|
||||
}
|
||||
|
||||
if (semver.eq(master.version, host.version)) {
|
||||
if ((await this.getXapi(host).listMissingPatches(master._xapiId)).length > 0) {
|
||||
log.error('master has missing patches', { masterId: master.id })
|
||||
throwError()
|
||||
}
|
||||
if (master.rebootRequired) {
|
||||
log.error('master needs to reboot')
|
||||
throwError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
packages/xo-server/src/api/pusb.mjs
Normal file
27
packages/xo-server/src/api/pusb.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
export async function scan({ host }) {
|
||||
await this.getXapi(host).call('PUSB.scan', host._xapiRef)
|
||||
}
|
||||
|
||||
scan.params = {
|
||||
host: { type: 'string' },
|
||||
}
|
||||
scan.resolve = {
|
||||
host: ['host', 'host', 'operate'],
|
||||
}
|
||||
|
||||
export async function set({ pusb, enabled }) {
|
||||
const xapi = this.getXapi(pusb)
|
||||
|
||||
if (enabled !== undefined && enabled !== pusb.passthroughEnabled) {
|
||||
await xapi.call('PUSB.set_passthrough_enabled', pusb._xapiRef, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
set.params = {
|
||||
id: { type: 'string' },
|
||||
enabled: { type: 'boolean', optional: true },
|
||||
}
|
||||
|
||||
set.resolve = {
|
||||
pusb: ['id', 'PUSB', 'administrate'],
|
||||
}
|
||||
@@ -889,6 +889,17 @@ const TRANSFORMS = {
|
||||
vm: link(obj, 'VM'),
|
||||
}
|
||||
},
|
||||
|
||||
pusb(obj) {
|
||||
return {
|
||||
type: 'PUSB',
|
||||
|
||||
description: obj.description,
|
||||
host: link(obj, 'host'),
|
||||
passthroughEnabled: obj.passthrough_enabled,
|
||||
usbGroup: link(obj, 'USB_group'),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -99,7 +99,7 @@ test('An ova file is generated correctly', async () => {
|
||||
try {
|
||||
await execXmllint(xml, [
|
||||
'--schema',
|
||||
path.join(__dirname, 'ova-schema', 'dsp8023_1.1.1.xsd'),
|
||||
path.join(__dirname, '..', 'src', 'ova-schema', 'dsp8023_1.1.1.xsd'),
|
||||
'--noout',
|
||||
'--nonet',
|
||||
'-',
|
||||
|
||||
@@ -54,13 +54,9 @@ export default class IsoDevice extends Component {
|
||||
() => this.props.vm.$pool,
|
||||
() => this.props.vm.$container,
|
||||
(vmPool, vmContainer) => sr => {
|
||||
const vmRunning = vmContainer !== vmPool
|
||||
const sameHost = vmContainer === sr.$container
|
||||
const samePool = vmPool === sr.$pool
|
||||
|
||||
return (
|
||||
samePool &&
|
||||
(vmRunning ? sr.shared || sameHost : true) &&
|
||||
vmPool === sr.$pool &&
|
||||
(sr.shared || vmContainer === sr.$container) &&
|
||||
(sr.SR_type === 'iso' || (sr.SR_type === 'udev' && sr.size))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import _ from 'intl'
|
||||
import Copiable from 'copiable'
|
||||
import decorate from 'apply-decorators'
|
||||
import Icon from 'icon'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
import store from 'store'
|
||||
import HomeTags from 'home-tags'
|
||||
@@ -24,10 +23,21 @@ export default decorate([
|
||||
provideState({
|
||||
computed: {
|
||||
areHostsVersionsEqual: ({ areHostsVersionsEqualByPool }, { host }) => areHostsVersionsEqualByPool[host.$pool],
|
||||
inMemoryVms: (_, { vms }) => {
|
||||
const result = []
|
||||
for (const key of Object.keys(vms)) {
|
||||
const vm = vms[key]
|
||||
const { power_state } = vm
|
||||
if (power_state === 'Running' || power_state === 'Paused') {
|
||||
result.push(vm)
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
},
|
||||
}),
|
||||
injectState,
|
||||
({ statsOverview, host, nVms, vmController, vms, state: { areHostsVersionsEqual } }) => {
|
||||
({ statsOverview, host, nVms, vmController, state: { areHostsVersionsEqual, inMemoryVms } }) => {
|
||||
const pool = getObject(store.getState(), host.$pool)
|
||||
const vmsFilter = encodeURIComponent(new CM.Property('$container', new CM.String(host.id)).toString())
|
||||
return (
|
||||
@@ -120,7 +130,7 @@ export default decorate([
|
||||
tooltip={`${host.productBrand} (${formatSize(vmController.memory.size)})`}
|
||||
value={vmController.memory.size}
|
||||
/>
|
||||
{map(vms, vm => (
|
||||
{inMemoryVms.map(vm => (
|
||||
<UsageElement
|
||||
tooltip={`${vm.name_label} (${formatSize(vm.memory.size)})`}
|
||||
key={vm.id}
|
||||
|
||||
Reference in New Issue
Block a user