Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xo-web,xo-server): implement ISO import #6180

Merged
merged 6 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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':
julien-f marked this conversation as resolved.
Show resolved Hide resolved
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.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other languages need to be updated too.

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