Skip to content

Commit

Permalink
Feat(import): implement iso import
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeauchamp committed Apr 20, 2022
1 parent 95ec592 commit 25fb509
Show file tree
Hide file tree
Showing 6 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 @@ -8,6 +8,7 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [VM export] Feat export to `ova` format (PR [#6006](https://github.com/vatesfr/xen-orchestra/pull/6006))
- [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
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 @@ -1583,7 +1583,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
8 changes: 6 additions & 2 deletions packages/xo-web/src/common/select-objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
import { addSubscriptions, connectStore, resolveResourceSets } from './utils'
import {
isSrWritable,
isSrWritableOrIso,
subscribeCloudConfigs,
subscribeCurrentUser,
subscribeGroups,
Expand Down Expand Up @@ -376,12 +377,15 @@ export const SelectPool = makeStoreSelect(
// ===================================================================

export const SelectSr = makeStoreSelect(
() => {
(_, { allowIsoSr } = {}) => {
const getPools = createGetObjectsOfType('pool')
const getHosts = createGetObjectsOfType('host')

const getSrsByContainer = createGetObjectsOfType('SR')
.filter((_, { predicate }) => predicate || isSrWritable)
.filter((_, { predicate }) => {
console.log(predicate)
return predicate || (allowIsoSr ? isSrWritableOrIso : isSrWritable)
})
.sort()
.groupBy('$container')

Expand Down
5 changes: 4 additions & 1 deletion 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 Expand Up @@ -1675,8 +1676,10 @@ const importDisk = async ({ description, file, name, type, vmdkData }, sr) => {
vmdkData,
})
formData.append('file', file)

const result = await post(res.$sendTo, formData)
const body = await result.json()
const text = await result.text()
const body = JSON.parse(text)
if (result.status !== 200) {
throw new Error(body.error.message)
}
Expand Down
5 changes: 3 additions & 2 deletions packages/xo-web/src/xo-app/disk-import/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const DiskImport = decorate([
initialState: getInitialState,
effects: {
handleDrop: async function (_, files) {
// @todo no setState ?
this.state.loadingDisks = true
const disks = await Promise.all(
map(files, async file => {
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,7 +109,7 @@ const DiskImport = decorate([
<Row>
<LabelCol>{_('importToSr')}</LabelCol>
<InputCol>
<SelectSr onChange={effects.onChangeSr} required value={sr} />
<SelectSr onChange={effects.onChangeSr} required value={sr} allowIsoSr />
</InputCol>
</Row>
{sr !== undefined && (
Expand Down

0 comments on commit 25fb509

Please sign in to comment.