feat(xo-web,xo-server): implement ISO import (#6180)
This commit is contained in:
parent
af85df611c
commit
0706e6f4ff
@ -11,6 +11,7 @@
|
|||||||
- [Backup] Add _Restore Health Check_: ensure a backup is viable by doing an automatic test restore (requires guest tools in the VM) [#6148](https://github.com/vatesfr/xen-orchestra/pull/6148)
|
- [Backup] Add _Restore Health Check_: ensure a backup is viable by doing an automatic test restore (requires guest tools in the VM) [#6148](https://github.com/vatesfr/xen-orchestra/pull/6148)
|
||||||
- [VM migrate] Allow to choose a private network for VIFs network (PR [#6200](https://github.com/vatesfr/xen-orchestra/pull/6200))
|
- [VM migrate] Allow to choose a private network for VIFs network (PR [#6200](https://github.com/vatesfr/xen-orchestra/pull/6200))
|
||||||
- [Proxy] Disable "Deploy proxy" button for source users (PR [#6199](https://github.com/vatesfr/xen-orchestra/pull/6199))
|
- [Proxy] Disable "Deploy proxy" button for source users (PR [#6199](https://github.com/vatesfr/xen-orchestra/pull/6199))
|
||||||
|
- [Import] Feat import `iso` disks (PR [#6180](https://github.com/vatesfr/xen-orchestra/pull/6180))
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { pipeline } from 'stream'
|
|||||||
import { checkFooter, peekFooterFromVhdStream } from 'vhd-lib'
|
import { checkFooter, peekFooterFromVhdStream } from 'vhd-lib'
|
||||||
import { vmdkToVhd } from 'xo-vmdk-to-vhd'
|
import { vmdkToVhd } from 'xo-vmdk-to-vhd'
|
||||||
|
|
||||||
import { VDI_FORMAT_VHD } from '../xapi/index.mjs'
|
import { VDI_FORMAT_VHD, VDI_FORMAT_RAW } from '../xapi/index.mjs'
|
||||||
|
|
||||||
const log = createLogger('xo:disk')
|
const log = createLogger('xo:disk')
|
||||||
|
|
||||||
@ -182,25 +182,37 @@ async function handleImport(req, res, { type, name, description, vmdkData, srId,
|
|||||||
})()
|
})()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
let diskFormat = VDI_FORMAT_VHD
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
part.length = part.byteCount
|
part.length = part.byteCount
|
||||||
if (type === 'vmdk') {
|
switch (type) {
|
||||||
vhdStream = await vmdkToVhd(part, vmdkData.grainLogicalAddressList, vmdkData.grainFileOffsetList)
|
case 'vmdk':
|
||||||
size = vmdkData.capacity
|
vhdStream = await vmdkToVhd(part, vmdkData.grainLogicalAddressList, vmdkData.grainFileOffsetList)
|
||||||
} else if (type === 'vhd') {
|
size = vmdkData.capacity
|
||||||
vhdStream = part
|
break
|
||||||
const footer = await peekFooterFromVhdStream(vhdStream)
|
case 'vhd':
|
||||||
try {
|
{
|
||||||
checkFooter(footer)
|
const footer = await peekFooterFromVhdStream(vhdStream)
|
||||||
} catch (e) {
|
try {
|
||||||
if (e instanceof assert.AssertionError) {
|
checkFooter(footer)
|
||||||
throw new JsonRpcError(`Vhd file had an invalid header ${e}`)
|
} catch (e) {
|
||||||
|
if (e instanceof assert.AssertionError) {
|
||||||
|
throw new JsonRpcError(`Vhd file had an invalid header ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vhdStream = part
|
||||||
|
size = footer.currentSize
|
||||||
}
|
}
|
||||||
}
|
break
|
||||||
size = footer.currentSize
|
case 'iso':
|
||||||
} else {
|
diskFormat = VDI_FORMAT_RAW
|
||||||
throw new JsonRpcError(`Unknown disk type, expected "vhd" or "vmdk", got ${type}`)
|
vhdStream = part
|
||||||
|
size = part.byteCount
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new JsonRpcError(`Unknown disk type, expected "iso", "vhd" or "vmdk", got ${type}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const vdi = await xapi.createVdi({
|
const vdi = await xapi.createVdi({
|
||||||
name_description: description,
|
name_description: description,
|
||||||
name_label: name,
|
name_label: name,
|
||||||
@ -208,7 +220,7 @@ async function handleImport(req, res, { type, name, description, vmdkData, srId,
|
|||||||
sr: srId,
|
sr: srId,
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
await xapi.importVdiContent(vdi, vhdStream, VDI_FORMAT_VHD)
|
await xapi.importVdiContent(vdi, vhdStream, { format: diskFormat })
|
||||||
res.end(format.response(0, vdi.$id))
|
res.end(format.response(0, vdi.$id))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await vdi.$destroy()
|
await vdi.$destroy()
|
||||||
|
@ -10,13 +10,15 @@ export default class Dropzone extends Component {
|
|||||||
onDrop: PropTypes.func,
|
onDrop: PropTypes.func,
|
||||||
message: PropTypes.node,
|
message: PropTypes.node,
|
||||||
multiple: PropTypes.bool,
|
multiple: PropTypes.bool,
|
||||||
|
accept: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onDrop, message, multiple } = this.props
|
const { onDrop, message, multiple, accept } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactDropzone
|
<ReactDropzone
|
||||||
|
accept={accept}
|
||||||
activeClassName={styles.activeDropzone}
|
activeClassName={styles.activeDropzone}
|
||||||
className={styles.dropzone}
|
className={styles.dropzone}
|
||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
|
@ -3819,7 +3819,7 @@ export default {
|
|||||||
diskImportSuccess: 'Importazione del disco riuscita',
|
diskImportSuccess: 'Importazione del disco riuscita',
|
||||||
|
|
||||||
// Original text: 'Drop VMDK or VHD files here to import disks.'
|
// Original text: 'Drop VMDK or VHD files here to import disks.'
|
||||||
dropDisksFiles: 'Rilascia qui i file VMDK o VHD per importare i dischi.',
|
dropDisksFiles: 'Rilascia qui i file ISO, VMDK o VHD per importare i dischi.',
|
||||||
|
|
||||||
// Original text: 'To SR'
|
// Original text: 'To SR'
|
||||||
importToSr: 'A SR',
|
importToSr: 'A SR',
|
||||||
|
@ -1584,7 +1584,7 @@ const messages = {
|
|||||||
// ---- Disk import ---
|
// ---- Disk import ---
|
||||||
diskImportFailed: 'Disk import failed',
|
diskImportFailed: 'Disk import failed',
|
||||||
diskImportSuccess: 'Disk import success',
|
diskImportSuccess: 'Disk import success',
|
||||||
dropDisksFiles: 'Drop VMDK or VHD files here to import disks.',
|
dropDisksFiles: 'Drop ISO, VMDK or VHD files here to import disks.',
|
||||||
importToSr: 'To SR',
|
importToSr: 'To SR',
|
||||||
|
|
||||||
// ---- Tasks ---
|
// ---- Tasks ---
|
||||||
|
@ -54,6 +54,7 @@ export const XEN_VIDEORAM_VALUES = [1, 2, 4, 8, 16]
|
|||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
export const isSrWritable = sr => sr && sr.content_type !== 'iso' && sr.size > 0
|
export const isSrWritable = sr => sr && sr.content_type !== 'iso' && sr.size > 0
|
||||||
|
export const isSrWritableOrIso = sr => sr && sr.size > 0
|
||||||
export const isSrShared = sr => sr && sr.shared
|
export const isSrShared = sr => sr && sr.shared
|
||||||
export const isVmRunning = vm => vm && vm.power_state === 'Running'
|
export const isVmRunning = vm => vm && vm.power_state === 'Running'
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import { InputCol, LabelCol, Row } from 'form-grid'
|
|||||||
import { map } from 'lodash'
|
import { map } from 'lodash'
|
||||||
import { readCapacityAndGrainTable } from 'xo-vmdk-to-vhd'
|
import { readCapacityAndGrainTable } from 'xo-vmdk-to-vhd'
|
||||||
import { SelectSr } from 'select-objects'
|
import { SelectSr } from 'select-objects'
|
||||||
|
import { isSrWritableOrIso } from '../../common/xo'
|
||||||
|
|
||||||
const getInitialState = () => ({
|
const getInitialState = () => ({
|
||||||
disks: [],
|
disks: [],
|
||||||
@ -40,7 +41,7 @@ const DiskImport = decorate([
|
|||||||
if (
|
if (
|
||||||
extIndex >= 0 &&
|
extIndex >= 0 &&
|
||||||
(type = name.slice(extIndex + 1).toLowerCase()) &&
|
(type = name.slice(extIndex + 1).toLowerCase()) &&
|
||||||
(type === 'vmdk' || type === 'vhd')
|
(type === 'vmdk' || type === 'vhd' || type === 'iso')
|
||||||
) {
|
) {
|
||||||
let vmdkData
|
let vmdkData
|
||||||
if (type === 'vmdk') {
|
if (type === 'vmdk') {
|
||||||
@ -108,12 +109,16 @@ const DiskImport = decorate([
|
|||||||
<Row>
|
<Row>
|
||||||
<LabelCol>{_('importToSr')}</LabelCol>
|
<LabelCol>{_('importToSr')}</LabelCol>
|
||||||
<InputCol>
|
<InputCol>
|
||||||
<SelectSr onChange={effects.onChangeSr} required value={sr} />
|
<SelectSr onChange={effects.onChangeSr} required value={sr} predicate={isSrWritableOrIso} />
|
||||||
</InputCol>
|
</InputCol>
|
||||||
</Row>
|
</Row>
|
||||||
{sr !== undefined && (
|
{sr !== undefined && (
|
||||||
<div>
|
<div>
|
||||||
<Dropzone onDrop={effects.handleDrop} message={_('dropDisksFiles')} />
|
<Dropzone
|
||||||
|
onDrop={effects.handleDrop}
|
||||||
|
message={_('dropDisksFiles')}
|
||||||
|
accept={sr.content_type === 'iso' ? '.iso' : ['.vhd', '.vmdk']}
|
||||||
|
/>
|
||||||
{loadingDisks && <Icon icon='loading' />}
|
{loadingDisks && <Icon icon='loading' />}
|
||||||
{disks.length > 0 && (
|
{disks.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
|
Loading…
Reference in New Issue
Block a user