Skip to content

Commit

Permalink
feat(xo-web,xo-server): implement ISO import (#6180)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeauchamp committed Apr 28, 2022
1 parent af85df6 commit 0706e6f
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [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))
- [Import] Feat import `iso` disks (PR [#6180](https://github.com/vatesfr/xen-orchestra/pull/6180))

### Bug fixes

Expand Down
46 changes: 29 additions & 17 deletions packages/xo-server/src/api/disk.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { pipeline } from 'stream'
import { checkFooter, peekFooterFromVhdStream } from 'vhd-lib'
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')

Expand Down Expand Up @@ -182,33 +182,45 @@ async function handleImport(req, res, { type, name, description, vmdkData, srId,
})()
)
} else {
let diskFormat = VDI_FORMAT_VHD
await Promise.all(promises)
part.length = part.byteCount
if (type === 'vmdk') {
vhdStream = await vmdkToVhd(part, vmdkData.grainLogicalAddressList, vmdkData.grainFileOffsetList)
size = vmdkData.capacity
} else if (type === 'vhd') {
vhdStream = part
const footer = await peekFooterFromVhdStream(vhdStream)
try {
checkFooter(footer)
} catch (e) {
if (e instanceof assert.AssertionError) {
throw new JsonRpcError(`Vhd file had an invalid header ${e}`)
switch (type) {
case 'vmdk':
vhdStream = await vmdkToVhd(part, vmdkData.grainLogicalAddressList, vmdkData.grainFileOffsetList)
size = vmdkData.capacity
break
case 'vhd':
{
const footer = await peekFooterFromVhdStream(vhdStream)
try {
checkFooter(footer)
} catch (e) {
if (e instanceof assert.AssertionError) {
throw new JsonRpcError(`Vhd file had an invalid header ${e}`)
}
}
vhdStream = part
size = footer.currentSize
}
}
size = footer.currentSize
} else {
throw new JsonRpcError(`Unknown disk type, expected "vhd" or "vmdk", got ${type}`)
break
case 'iso':
diskFormat = VDI_FORMAT_RAW
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({
name_description: description,
name_label: name,
size,
sr: srId,
})
try {
await xapi.importVdiContent(vdi, vhdStream, VDI_FORMAT_VHD)
await xapi.importVdiContent(vdi, vhdStream, { format: diskFormat })
res.end(format.response(0, vdi.$id))
} catch (e) {
await vdi.$destroy()
Expand Down
4 changes: 3 additions & 1 deletion packages/xo-web/src/common/dropzone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ export default class Dropzone extends Component {
onDrop: PropTypes.func,
message: PropTypes.node,
multiple: PropTypes.bool,
accept: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
}

render() {
const { onDrop, message, multiple } = this.props
const { onDrop, message, multiple, accept } = this.props

return (
<ReactDropzone
accept={accept}
activeClassName={styles.activeDropzone}
className={styles.dropzone}
multiple={multiple}
Expand Down
2 changes: 1 addition & 1 deletion packages/xo-web/src/common/intl/locales/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -3819,7 +3819,7 @@ export default {
diskImportSuccess: 'Importazione del disco riuscita',

// 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'
importToSr: 'A SR',
Expand Down
2 changes: 1 addition & 1 deletion packages/xo-web/src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -1584,7 +1584,7 @@ const messages = {
// ---- Disk import ---
diskImportFailed: 'Disk import failed',
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',

// ---- Tasks ---
Expand Down
1 change: 1 addition & 0 deletions packages/xo-web/src/common/xo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 isSrWritableOrIso = sr => sr && sr.size > 0
export const isSrShared = sr => sr && sr.shared
export const isVmRunning = vm => vm && vm.power_state === 'Running'

Expand Down
11 changes: 8 additions & 3 deletions packages/xo-web/src/xo-app/disk-import/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { InputCol, LabelCol, Row } from 'form-grid'
import { map } from 'lodash'
import { readCapacityAndGrainTable } from 'xo-vmdk-to-vhd'
import { SelectSr } from 'select-objects'
import { isSrWritableOrIso } from '../../common/xo'

const getInitialState = () => ({
disks: [],
Expand All @@ -40,7 +41,7 @@ const DiskImport = decorate([
if (
extIndex >= 0 &&
(type = name.slice(extIndex + 1).toLowerCase()) &&
(type === 'vmdk' || type === 'vhd')
(type === 'vmdk' || type === 'vhd' || type === 'iso')
) {
let vmdkData
if (type === 'vmdk') {
Expand Down Expand Up @@ -108,12 +109,16 @@ const DiskImport = decorate([
<Row>
<LabelCol>{_('importToSr')}</LabelCol>
<InputCol>
<SelectSr onChange={effects.onChangeSr} required value={sr} />
<SelectSr onChange={effects.onChangeSr} required value={sr} predicate={isSrWritableOrIso} />
</InputCol>
</Row>
{sr !== undefined && (
<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' />}
{disks.length > 0 && (
<div>
Expand Down

0 comments on commit 0706e6f

Please sign in to comment.