Compare commits

..

1 Commits

Author SHA1 Message Date
Florent Beauchamp
ca8d6b4885 demo 2024-02-21 13:35:48 +00:00
10 changed files with 99 additions and 161 deletions

View File

@@ -141,6 +141,7 @@ export class Task {
}
#log(event, props) {
console.log({taskId: this.#id,event})
this.#onLog({
...props,
event,

View File

@@ -88,49 +88,89 @@ 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 } }
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 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)
}
})
}
const { concurrency } = settings
await asyncMapSettled(vmIds, concurrency === 0 ? handleVm : limitConcurrency(concurrency)(handleVm))
if(toHandle.length > 0){
console.log('LAST RUN ')
// last run will really fail this time
await asyncMapSettled(toHandle, fn=>fn())
}
}
)
}

View File

@@ -64,6 +64,11 @@ 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,

View File

@@ -15,31 +15,6 @@
&.dark {
color-scheme: dark;
}
--scrollbar-width: 1.4rem;
--scrollbar-border: 0.4rem;
--scrollbar-thumb-color: var(--color-purple-l40);
--scrollbar-track-color: var(--background-color-purple-10);
/* Firefox/Gecko and Chrome versions >= 121 */
scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);
scrollbar-width: auto;
/* Chrome/Webkit */
::-webkit-scrollbar {
width: var(--scrollbar-width);
}
::-webkit-scrollbar-track {
background-color: var(--scrollbar-track-color);
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb-color);
border: var(--scrollbar-border) solid transparent;
border-radius: 90rem;
background-clip: content-box;
}
}
a {
@@ -76,15 +51,3 @@ pre {
.link.router-link-active {
text-decoration: underline;
}
.scrollbar-small {
scrollbar-width: thin;
&::-webkit-scrollbar {
--scrollbar-width: 0.7rem;
}
&::-webkit-scrollbar-thumb {
--scrollbar-border: 0.2rem;
}
}

View File

@@ -22,7 +22,6 @@
- [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

View File

@@ -1,4 +1,3 @@
import semver from 'semver'
import { createLogger } from '@xen-orchestra/log'
import assert from 'assert'
import { format } from 'json-rpc-peer'
@@ -137,38 +136,13 @@ export async function restart({
const pool = this.getObject(host.$poolId, 'pool')
const master = this.getObject(pool.master, 'host')
const hostRebootRequired = host.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()
}
}
if (hostRebootRequired && host.id !== master.id && host.version === master.version) {
throw incorrectState({
actual: hostRebootRequired,
expected: false,
object: master.id,
property: 'rebootRequired',
})
}
}

View File

@@ -1,27 +0,0 @@
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'],
}

View File

@@ -889,17 +889,6 @@ 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'),
}
},
}
// ===================================================================

View File

@@ -54,9 +54,13 @@ 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 (
vmPool === sr.$pool &&
(sr.shared || vmContainer === sr.$container) &&
samePool &&
(vmRunning ? sr.shared || sameHost : true) &&
(sr.SR_type === 'iso' || (sr.SR_type === 'udev' && sr.size))
)
}

View File

@@ -3,6 +3,7 @@ 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'
@@ -23,21 +24,10 @@ 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, state: { areHostsVersionsEqual, inMemoryVms } }) => {
({ statsOverview, host, nVms, vmController, vms, state: { areHostsVersionsEqual } }) => {
const pool = getObject(store.getState(), host.$pool)
const vmsFilter = encodeURIComponent(new CM.Property('$container', new CM.String(host.id)).toString())
return (
@@ -130,7 +120,7 @@ export default decorate([
tooltip={`${host.productBrand} (${formatSize(vmController.memory.size)})`}
value={vmController.memory.size}
/>
{inMemoryVms.map(vm => (
{map(vms, vm => (
<UsageElement
tooltip={`${vm.name_label} (${formatSize(vm.memory.size)})`}
key={vm.id}