Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de4efdaf7a |
@@ -437,8 +437,7 @@ export async function cleanVm(
|
||||
}
|
||||
}
|
||||
|
||||
// no warning because a VHD can be unused for perfectly good reasons,
|
||||
// e.g. the corresponding backup (metadata file) has been deleted
|
||||
logWarn('unused VHD', { path: vhd })
|
||||
if (remove) {
|
||||
logInfo('deleting unused VHD', { path: vhd })
|
||||
unusedVhdsDeletion.push(VhdAbstract.unlink(handler, vhd))
|
||||
|
||||
@@ -7,3 +7,4 @@ export { default as VDI } from './vdi.mjs'
|
||||
export { default as VIF } from './vif.mjs'
|
||||
export { default as VM } from './vm.mjs'
|
||||
export { default as VTPM } from './vtpm.mjs'
|
||||
export { default as VUSB } from './vusb.mjs'
|
||||
|
||||
16
@xen-orchestra/xapi/vusb.mjs
Normal file
16
@xen-orchestra/xapi/vusb.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import ignoreErrors from 'promise-toolbox/ignoreErrors'
|
||||
|
||||
export default class Vusb {
|
||||
async create(VM, USB_group) {
|
||||
return this.call('VUSB.create', VM, USB_group)
|
||||
}
|
||||
|
||||
async unplug(ref) {
|
||||
await this.call('VUSB.unplug', ref)
|
||||
}
|
||||
|
||||
async destroy(ref) {
|
||||
await ignoreErrors.call(this.VUSB_unplug(ref))
|
||||
await this.call('VUSB.destroy', ref)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
- [Home & REST API] `$container` field of an halted VM now points to a host if a VDI is on a local storage [Forum#71769](https://xcp-ng.org/forum/post/71769)
|
||||
- [Size Input] Ability to select two new units in the dropdown (`TiB`, `PiB`) (PR [#7382](https://github.com/vatesfr/xen-orchestra/pull/7382))
|
||||
|
||||
|
||||
### Bug fixes
|
||||
|
||||
> Users must be able to say: “I had this issue, happy to know it's fixed”
|
||||
@@ -22,8 +23,6 @@
|
||||
- [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))
|
||||
- [New/VM] Respect _Fast clone_ setting broken since 5.91.0 (PR [#7388](https://github.com/vatesfr/xen-orchestra/issues/7388))
|
||||
- [Backup] Remove incorrect _unused VHD_ warning because the situation is normal (PR [#7406](https://github.com/vatesfr/xen-orchestra/issues/7406))
|
||||
|
||||
### Packages to release
|
||||
|
||||
|
||||
@@ -425,32 +425,6 @@ It works even if the VM is running, because we'll automatically export a snapsho
|
||||
|
||||
In the VM "Snapshots" tab, you can also export a snapshot like you export a VM.
|
||||
|
||||
## VM migration
|
||||
|
||||
### Simple VM Migration (VM.pool_migrate)
|
||||
|
||||
In simple migration, the VM's active state is transferred from host A to host B while its disks remains in its original location. This feature is only possible when the VM's disks are on a shared SR by both hosts and if the VM is running.
|
||||
|
||||
#### Use Case
|
||||
|
||||
- Migrate a VM within the same pool from host A to host B without moving the VM's VDIs.
|
||||
|
||||
### VM Migration with Storage Motion (VM.migrate_send)
|
||||
|
||||
VM migration with storage motion allows you to migrate a VM from one host to another when the VM's disks are not on a shared SR between the two hosts or if a specific network is chosen for the migration. VDIs will be migrated to the destination SR if one is provided.
|
||||
|
||||
#### Use Cases
|
||||
|
||||
- Migrate a VM to another pool.
|
||||
- Migrate a VM within the same pool from host A to host B by selecting a network for the migration.
|
||||
- Migrate a VM within the same pool from host A to host B by moving the VM's VDIs to another storage.
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
- Migrating a VM that has VDIs on a shared SR from host A to host B must trigger a "Simple VM Migration".
|
||||
- Migrating a VM that has VDIs on a shared SR from host A to host B using a particular network must trigger a "VM Migration with Storage Motion" without moving its VDIs.
|
||||
- Migrating a VM from host A to host B with a destination SR must trigger a "VM Migration with Storage Motion" and move VDIs to the destination SR, regardless of where the VDIs were stored.
|
||||
|
||||
## Hosts management
|
||||
|
||||
Outside updates (see next section), you can also do host management via Xen Orchestra. Basic operations are supported, like reboot, shutdown and so on.
|
||||
|
||||
@@ -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'],
|
||||
}
|
||||
42
packages/xo-server/src/api/vusb.mjs
Normal file
42
packages/xo-server/src/api/vusb.mjs
Normal file
@@ -0,0 +1,42 @@
|
||||
// Creates a VUSB which will be plugged to the VM at its next restart
|
||||
// Only one VUSB can be attached to a given USB_group, and up to six VUSB can be attached to a VM.
|
||||
export async function create({ vm, usbGroup }) {
|
||||
const xapi = this.getXapi(vm)
|
||||
const vusbRef = await xapi.VUSB_create(vm._xapiRef, usbGroup._xapiRef)
|
||||
return xapi.getField('VUSB', vusbRef, 'uuid')
|
||||
}
|
||||
|
||||
create.params = {
|
||||
vmId: { type: 'string' },
|
||||
usbGroupId: { type: 'string' },
|
||||
}
|
||||
|
||||
create.resolve = {
|
||||
vm: ['vmId', 'VM', 'administrate'],
|
||||
usbGroup: ['usbGroupId', 'USB_group', 'administrate'],
|
||||
}
|
||||
|
||||
// Unplug VUSB until next VM restart
|
||||
export async function unplug({ vusb }) {
|
||||
await this.getXapi(vusb).VUSB_unplug(vusb._xapiRef)
|
||||
}
|
||||
|
||||
unplug.params = {
|
||||
id: { type: 'string' },
|
||||
}
|
||||
|
||||
unplug.resolve = {
|
||||
vusb: ['id', 'VUSB', 'administrate'],
|
||||
}
|
||||
|
||||
export async function destroy({ vusb }) {
|
||||
await this.getXapi(vusb).VUSB_destroy(vusb._xapiRef)
|
||||
}
|
||||
|
||||
destroy.params = {
|
||||
id: { type: 'string' },
|
||||
}
|
||||
|
||||
destroy.resolve = {
|
||||
vusb: ['id', 'VUSB', 'administrate'],
|
||||
}
|
||||
@@ -882,6 +882,8 @@ const TRANSFORMS = {
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
vtpm(obj) {
|
||||
return {
|
||||
type: 'VTPM',
|
||||
@@ -890,16 +892,31 @@ const TRANSFORMS = {
|
||||
}
|
||||
},
|
||||
|
||||
pusb(obj) {
|
||||
return {
|
||||
type: 'PUSB',
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
description: obj.description,
|
||||
host: link(obj, 'host'),
|
||||
passthroughEnabled: obj.passthrough_enabled,
|
||||
vusb(obj) {
|
||||
return {
|
||||
type: 'VUSB',
|
||||
|
||||
vm: link(obj, 'VM'),
|
||||
currentlyAttached: obj.currently_attached,
|
||||
usbGroup: link(obj, 'USB_group'),
|
||||
}
|
||||
},
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
usb_group(obj) {
|
||||
return {
|
||||
type: 'USB_group',
|
||||
|
||||
PUSB: link(obj, 'PUSBs'),
|
||||
VUSB: link(obj, 'VUSBs'),
|
||||
nameDescription: obj.name_description,
|
||||
nameLabel: obj.name_label,
|
||||
otherConfig: obj.other_config,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ export const SelectHostVm = makeStoreSelect(
|
||||
|
||||
export const SelectVmTemplate = makeStoreSelect(
|
||||
() => {
|
||||
const getVmTemplatesByPool = createGetObjectsOfType('VM-template').filter(getPredicate).sort().groupBy('$pool')
|
||||
const getVmTemplatesByPool = createGetObjectsOfType('VM-template').filter(getPredicate).sort().groupBy('$container')
|
||||
const getPools = createGetObjectsOfType('pool')
|
||||
.pick(createSelector(getVmTemplatesByPool, vmTemplatesByPool => keys(vmTemplatesByPool)))
|
||||
.sort()
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -300,7 +300,7 @@ export default class NewVm extends BaseComponent {
|
||||
|
||||
get _isDiskTemplate() {
|
||||
const { template } = this.props
|
||||
return template && template.$VBDs.length !== 0 && template.name_label !== 'Other install media'
|
||||
return template && template.template_info.disks.length === 0 && template.name_label !== 'Other install media'
|
||||
}
|
||||
_setState = (newValues, callback) => {
|
||||
this.setState(
|
||||
@@ -470,7 +470,7 @@ export default class NewVm extends BaseComponent {
|
||||
|
||||
const data = {
|
||||
affinityHost: state.affinityHost && state.affinityHost.id,
|
||||
clone: this._isDiskTemplate && state.fastClone,
|
||||
clone: !this._isDiskTemplate && state.fastClone,
|
||||
existingDisks: state.existingDisks,
|
||||
installation,
|
||||
name_label: state.name_label,
|
||||
|
||||
Reference in New Issue
Block a user