Compare commits

...

66 Commits

Author SHA1 Message Date
Mohamedox
28523e591a fix 2019-09-26 16:43:11 +02:00
Mohamedox
e935cf8283 Adapt PR to comments 2019-09-26 16:18:44 +02:00
Mohamedox
87db911e5a delete forgotten comment 2019-09-26 15:57:40 +02:00
Mohamedox
ec6d3fd128 fix 2019-09-26 15:50:06 +02:00
Mohamedox
3aaafef88e adapt PR to comments 2019-09-26 15:22:38 +02:00
Mohamedox
714e4f4ea2 update changelog 2019-09-26 11:05:08 +02:00
Mohamedox
77b0914e48 fix 2019-09-26 10:31:28 +02:00
Mohamedox
5c4a362529 adapt PR to comments 2019-09-26 10:29:09 +02:00
Mohamedox
d7bcdfac19 fix 2019-09-25 17:20:32 +02:00
Mohamedox
5a3e895ce5 fix 2019-09-25 17:19:31 +02:00
Mohamedox
a7d08ac91e fix 2019-09-25 17:11:27 +02:00
Mohamedox
7ff48f3aa9 last fix 2019-09-25 16:38:27 +02:00
Mohamedox
7f42ab15dd fix 2019-09-25 16:22:35 +02:00
Mohamedox
3b7d02de95 fix 2019-09-25 16:17:43 +02:00
Mohamedox
3fdac01eb8 fix 2019-09-25 15:18:09 +02:00
Mohamedox
5601740334 fix 2019-09-25 14:48:50 +02:00
Mohamedox
f824cbe710 adapt PR to comments 2019-09-25 14:38:41 +02:00
Mohamedox
510f30eb23 fix 2019-09-25 13:38:56 +02:00
Mohamedox
c84eded0aa Adapt PR to comments 2019-09-25 11:59:10 +02:00
Mohamedox
02ed02926b update changelog 2019-09-25 10:17:56 +02:00
Mohamedox
77c172fce0 adapt Pr to comments 2019-09-25 10:17:55 +02:00
Mohamedox
c48e017711 fix 2019-09-25 10:17:55 +02:00
Mohamedox
855ec06628 Adapt PR to comments 2019-09-25 10:17:54 +02:00
Mohamedox
9e4b606372 fix 2019-09-25 10:17:32 +02:00
Mohamedox
de1c8f4d4f adapt PR to comments 2019-09-25 10:17:32 +02:00
Mohamedox
876562570c Adapt PR to comments 2019-09-25 10:17:31 +02:00
Mohamedox
bab514ffc4 fix 2019-09-25 10:17:00 +02:00
Mohamedox
dc15965972 fix 2019-09-25 10:16:59 +02:00
Mohamedox
903b7fed50 adapt PR to comments 2019-09-25 10:16:58 +02:00
Mohamedox
f62bdeae1b adapt PR to comments 2019-09-25 10:16:58 +02:00
Mohamedox
e259e72c92 fix 2019-09-25 10:16:57 +02:00
Mohamedox
2a64a4810b adapt PR to comments 2019-09-25 10:16:57 +02:00
Mohamedox
a543e05561 adapt pr to comments 2019-09-25 10:16:56 +02:00
Mohamedox
2c7ca39a77 Display error when pool has no default SR 2019-09-25 10:16:56 +02:00
Mohamedox
9ada70a5ab fix 2019-09-25 10:16:55 +02:00
Mohamedox
a574f9d8dc initialize template when changing page 2019-09-25 10:16:32 +02:00
Mohamedox
9a4f9a8978 fix 2019-09-25 10:16:31 +02:00
Mohamedox
1cdf14e587 fix 2019-09-25 10:16:30 +02:00
Mohamedox
9272524264 xva-store v1 2019-09-25 10:16:29 +02:00
Mohamedox
027dfd262f adapt PR to new specs 2019-09-25 10:16:28 +02:00
Mohamedox
8967ad94dc version 1.0 2019-09-25 10:16:27 +02:00
Mohamedox
48a0684097 xva store with popularity 2019-09-25 10:16:27 +02:00
Mohamedox
1caa98ea8b wip 2019-09-25 10:16:26 +02:00
Mohamedox
01e15ae4ec fix 2019-09-25 10:16:26 +02:00
Mohamedox
5077de953d adapt to new specs 2019-09-25 10:16:25 +02:00
Mohamedox
fab3751734 wip 2019-09-25 10:16:24 +02:00
Mohamedox
97df5d9e32 adapt PR to comments 2019-09-25 10:16:24 +02:00
Mohamedox
e2180303da change routing 2019-09-25 10:16:23 +02:00
Mohamedox
e2ddb62e5f mutualize code 2019-09-25 10:16:23 +02:00
Mohamedox
9e4265ae72 update changelog 2019-09-25 10:16:22 +02:00
Mohamedox
ec30e20278 fix 2019-09-25 10:15:51 +02:00
Mohamedox
1bf41b915c fix 2019-09-25 10:15:50 +02:00
Mohamedox
03e8cd0f7b change permission 2019-09-25 10:15:49 +02:00
Mohamedox
bf03dbd8dc fix 2019-09-25 10:15:49 +02:00
Mohamedox
570c9f6e0f enhance code quality 2019-09-25 10:15:48 +02:00
Mohamedox
e7016a4a40 fix 2019-09-25 10:15:47 +02:00
Mohamedox
f980ad58d4 xva 2019-09-25 10:15:46 +02:00
Mohamedox
a8af5a166d must subscribe 2019-09-25 10:15:46 +02:00
Mohamedox
d0acefaa04 poc version 2019-09-25 10:15:45 +02:00
Mohamedox
54fcc2e0db progress bar 2019-09-25 10:15:44 +02:00
Mohamedox
2d3f02cbbd wip 2019-09-25 10:15:43 +02:00
Mohamedox
13dd57bad7 only registred customer can download 2019-09-25 10:15:42 +02:00
Mohamedox
d9e26d155c wip 2019-09-25 10:15:41 +02:00
Mohamedox
c9556c44c9 WIP: feat(xo-web/hub): XO Vms HUB 2019-09-25 10:15:41 +02:00
Mohamedox
916b3ec662 adapt PR to comments 2019-09-25 10:15:40 +02:00
Mohamedox
f1cff1275c feat(xo-web/new-vm): create new VM with predefined template ID in URL query string
Fixes #4494
2019-09-25 10:15:39 +02:00
14 changed files with 548 additions and 40 deletions

View File

@@ -12,6 +12,7 @@
- [New VM] Cloud Init available for all plans (PR [#4543](https://github.com/vatesfr/xen-orchestra/pull/4543))
- [Servers] IPv6 addresses can be used [#4520](https://github.com/vatesfr/xen-orchestra/issues/4520) (PR [#4521](https://github.com/vatesfr/xen-orchestra/pull/4521)) \
Note: They must enclosed in brackets to differentiate with the port, e.g.: `[2001:db8::7334]` or `[ 2001:db8::7334]:4343`
- [HUB] VM template store [#1918](https://github.com/vatesfr/xen-orchestra/issues/1918) (PR [#4442](https://github.com/vatesfr/xen-orchestra/pull/4442))
### Bug fixes
@@ -27,5 +28,6 @@
> Rule of thumb: add packages on top.
- xen-api v0.27.2
- xo-server-cloud v0.3.0
- xo-server v5.51.0
- xo-web v5.51.0

View File

@@ -20,9 +20,13 @@ class XoServerCloud {
}
async load() {
const getResourceCatalog = () => this._getCatalog()
getResourceCatalog.description = 'Get the list of all available resources'
const getResourceCatalog = this._getCatalog.bind(this)
getResourceCatalog.description =
"Get the list of user's available resources"
getResourceCatalog.permission = 'admin'
getResourceCatalog.params = {
filters: { type: 'object', optional: true },
}
const registerResource = ({ namespace }) =>
this._registerResource(namespace)
@@ -34,8 +38,29 @@ class XoServerCloud {
}
registerResource.permission = 'admin'
const downloadAndInstallResource = this._downloadAndInstallResource.bind(
this
)
downloadAndInstallResource.description =
'Download and install a resource via cloud plugin'
downloadAndInstallResource.params = {
id: { type: 'string' },
namespace: { type: 'string' },
version: { type: 'string' },
sr: { type: 'string' },
}
downloadAndInstallResource.resolve = {
sr: ['sr', 'SR', 'administrate'],
}
downloadAndInstallResource.permission = 'admin'
this._unsetApiMethods = this._xo.addApiMethods({
cloud: {
downloadAndInstallResource,
getResourceCatalog,
registerResource,
},
@@ -66,8 +91,8 @@ class XoServerCloud {
// ----------------------------------------------------------------
async _getCatalog() {
const catalog = await this._updater.call('getResourceCatalog')
async _getCatalog({ filters } = {}) {
const catalog = await this._updater.call('getResourceCatalog', { filters })
if (!catalog) {
throw new Error('cannot get catalog')
@@ -90,6 +115,26 @@ class XoServerCloud {
// ----------------------------------------------------------------
async _downloadAndInstallResource({ id, namespace, sr, version }) {
const stream = await this._requestResource({
hub: true,
id,
namespace,
version,
})
const vm = await this._xo.getXapi(sr.$poolId).importVm(stream, {
srId: sr.id,
type: 'xva',
})
await vm.update_other_config({
'xo:resource:namespace': namespace,
'xo:resource:xva:version': version,
'xo:resource:xva:id': id,
})
}
// ----------------------------------------------------------------
async _registerResource(namespace) {
const _namespace = (await this._getNamespaces())[namespace]
@@ -106,8 +151,10 @@ class XoServerCloud {
// ----------------------------------------------------------------
async _getNamespaceCatalog(namespace) {
const namespaceCatalog = (await this._getCatalog())[namespace]
async _getNamespaceCatalog({ hub, namespace }) {
const namespaceCatalog = (await this._getCatalog({ filters: { hub } }))[
namespace
]
if (!namespaceCatalog) {
throw new Error(`cannot get catalog: ${namespace} not registered`)
@@ -118,14 +165,17 @@ class XoServerCloud {
// ----------------------------------------------------------------
async _requestResource(namespace, id, version) {
async _requestResource({ hub = false, id, namespace, version }) {
const _namespace = (await this._getNamespaces())[namespace]
if (!_namespace || !_namespace.registered) {
if (!hub && (!_namespace || !_namespace.registered)) {
throw new Error(`cannot get resource: ${namespace} not registered`)
}
const { _token: token } = await this._getNamespaceCatalog(namespace)
const { _token: token } = await this._getNamespaceCatalog({
hub,
namespace,
})
// 2018-03-20 Extra check: getResourceDownloadToken seems to be called without a token in some cases
if (token === undefined) {

View File

@@ -1164,11 +1164,11 @@ async function _prepareGlusterVm(
}
async function _importGlusterVM(xapi, template, lvmsrId) {
const templateStream = await this.requestResource(
'xosan',
template.id,
template.version
)
const templateStream = await this.requestResource({
id: template.id,
namespace: 'xosan',
version: template.version,
})
const newVM = await xapi.importVm(templateStream, {
srId: lvmsrId,
type: 'xva',
@@ -1535,8 +1535,11 @@ export async function downloadAndInstallXosanPack({ id, version, pool }) {
}
const xapi = this.getXapi(pool.id)
const res = await this.requestResource('xosan', id, version)
const res = await this.requestResource({
id,
namespace: 'xosan',
version,
})
await xapi.installSupplementalPackOnAllHosts(res)
await xapi.pool.update_other_config(
'xosan_pack_installation_time',

View File

@@ -2143,6 +2143,21 @@ const messages = {
xosanIssueHostNotInNetwork:
'Will configure the host xosan network device with a static IP address and plug it in.',
// Hub
hubPage: 'Hub',
noDefaultSr: 'The selected pool has no default SR',
successfulInstall: 'VM installed successfully',
vmNoAvailable: 'No VMs available ',
create: 'Create',
hubResourceAlert: 'Resource alert',
os: 'OS',
version: 'Version',
size: 'Size',
totalDiskSize: 'Total disk size',
hideInstalledPool: 'Already installed templates are hidden',
hubSrErrorTitle: 'Missing property',
hubImportNotificationTitle: 'XVA import',
// Licenses
xosanUnregisteredDisclaimer:
'You are not registered and therefore will not be able to create or manage your XOSAN SRs. {link}',

View File

@@ -58,3 +58,11 @@ export const setHomeVmIdsSelection = createAction(
'SET_HOME_VM_IDS_SELECTION',
homeVmIdsSelection => homeVmIdsSelection
)
export const markHubResourceAsInstalling = createAction(
'MARK_HUB_RESOURCE_AS_INSTALLING',
id => id
)
export const markHubResourceAsInstalled = createAction(
'MARK_HUB_RESOURCE_AS_INSTALLED',
id => id
)

View File

@@ -1,4 +1,5 @@
import cookies from 'cookies-js'
import { omit } from 'lodash'
import invoke from '../invoke'
@@ -92,6 +93,19 @@ export default {
homeVmIdsSelection,
}),
// whether a resource is currently being installed: `hubInstallingResources[<template id>]`
hubInstallingResources: combineActionHandlers(
{},
{
[actions.markHubResourceAsInstalling]: (
prevHubInstallingResources,
id
) => ({ ...prevHubInstallingResources, [id]: true }),
[actions.markHubResourceAsInstalled]: (prevHubInstallingResources, id) =>
omit(prevHubInstallingResources, id),
}
),
objects: combineActionHandlers(
{
all: {}, // Mutable for performance!

View File

@@ -349,6 +349,10 @@ export const subscribeResourceCatalog = createSubscription(() =>
_call('cloud.getResourceCatalog')
)
export const subscribeHubResourceCatalog = createSubscription(() =>
_call('cloud.getResourceCatalog', { filters: { hub: true } })
)
const getNotificationCookie = () => {
const notificationCookie = cookies.get(
`notifications:${store.getState().user.id}`
@@ -2871,7 +2875,10 @@ export const fixHostNotInXosanNetwork = (xosanSr, host) =>
// XOSAN packs -----------------------------------------------------------------
export const getResourceCatalog = () => _call('cloud.getResourceCatalog')
export const getResourceCatalog = ({ filters } = {}) =>
_call('cloud.getResourceCatalog', { filters })
export const getAllResourceCatalog = () => _call('cloud.getAllResourceCatalog')
const downloadAndInstallXosanPack = (pack, pool, { version }) =>
_call('xosan.downloadAndInstallXosanPack', {
@@ -2880,6 +2887,14 @@ const downloadAndInstallXosanPack = (pack, pool, { version }) =>
pool: resolveId(pool),
})
export const downloadAndInstallResource = ({ namespace, id, version, sr }) =>
_call('cloud.downloadAndInstallResource', {
namespace,
id,
version,
sr: resolveId(sr),
})
import UpdateXosanPacksModal from './update-xosan-packs-modal' // eslint-disable-line import/first
export const updateXosanPacks = pool =>
confirm({

View File

@@ -277,6 +277,10 @@
@extend .fa;
@extend .fa-thumbs-up;
}
&-deploy {
@extend .fa;
@extend .fa-rocket;
}
// Backups
&-backup {
@@ -886,6 +890,10 @@
@extend .fa;
@extend .fa-database;
}
&-menu-hub {
@extend .fa;
@extend .fa-cubes;
}
// New VM
&-new-vm {
&-infos {

View File

@@ -0,0 +1,63 @@
import _ from 'intl'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import { addSubscriptions, adminOnly } from 'utils'
import { Container, Col, Row } from 'grid'
import { injectState, provideState } from 'reaclette'
import { isEmpty, map, omit, orderBy } from 'lodash'
import { subscribeHubResourceCatalog } from 'xo'
import Page from '../page'
import Resource from './resource'
// ==================================================================
const HEADER = (
<h2>
<Icon icon='menu-hub' /> {_('hubPage')}
</h2>
)
export default decorate([
adminOnly,
addSubscriptions({
catalog: subscribeHubResourceCatalog,
}),
provideState({
computed: {
resources: (_, { catalog }) =>
orderBy(
map(omit(catalog, '_namespaces'), (entry, namespace) => ({
namespace,
...entry.xva,
})),
'name',
'asc'
),
},
}),
injectState,
({ state: { resources } }) => (
<Page header={HEADER} title='hubPage' formatTitle>
<Container>
<Row>
{isEmpty(resources) ? (
<Col>
<h2 className='text-muted'>
&nbsp; {_('vmNoAvailable')}
<Icon icon='alarm' color='yellow' />
</h2>
</Col>
) : (
resources.map(data => (
<Col key={data.namespace} mediumSize={3}>
<Resource {...data} />
</Col>
))
)}
</Row>
</Container>
</Page>
),
])

View File

@@ -0,0 +1,60 @@
import * as FormGrid from 'form-grid'
import _ from 'intl'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import Tooltip from 'tooltip'
import { Container } from 'grid'
import { SelectPool } from 'select-objects'
import { error } from 'notification'
import { injectState, provideState } from 'reaclette'
export default decorate([
provideState({
initialState: ({ multi }) => ({
pools: multi ? [] : undefined,
}),
effects: {
onChangePool(__, pools) {
const noDefaultSr = Array.isArray(pools)
? pools.some(pool => pool.default_SR === undefined)
: pools.default_SR === undefined
if (noDefaultSr) {
error(_('hubSrErrorTitle'), _('noDefaultSr'))
} else {
this.props.onChange({
pools,
pool: pools,
})
return {
pools,
}
}
},
},
}),
injectState,
({ effects, install, multi, state, poolPredicate }) => (
<Container>
<FormGrid.Row>
<label>
{_('vmImportToPool')}
&nbsp;
{install && (
<Tooltip content={_('hideInstalledPool')}>
<Icon icon='info' />
</Tooltip>
)}
</label>
<SelectPool
className='mb-1'
multi={multi}
onChange={effects.onChangePool}
predicate={poolPredicate}
required
value={state.pools}
/>
</FormGrid.Row>
</Container>
),
])

View File

@@ -0,0 +1,255 @@
import _ from 'intl'
import ActionButton from 'action-button'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import { Card, CardBlock, CardHeader } from 'card'
import { Col, Row } from 'grid'
import { alert, form } from 'modal'
import { connectStore, formatSize, getXoaPlan } from 'utils'
import { createGetObjectsOfType } from 'selectors'
import { downloadAndInstallResource, deleteTemplates } from 'xo'
import { error, success } from 'notification'
import { find, filter } from 'lodash'
import { injectState, provideState } from 'reaclette'
import { withRouter } from 'react-router'
import ResourceForm from './resource-form'
const subscribeAlert = () =>
alert(
_('hubResourceAlert'),
<div>
<p>
{_('considerSubscribe', {
link: 'https://xen-orchestra.com',
})}
</p>
</div>
)
export default decorate([
withRouter,
connectStore(() => {
const getTemplates = createGetObjectsOfType('VM-template').sort()
const getPools = createGetObjectsOfType('pool')
return {
templates: getTemplates,
pools: getPools,
hubInstallingResources: state => state.hubInstallingResources,
}
}),
provideState({
initialState: () => ({
selectedInstallPools: [],
}),
effects: {
async install() {
const {
id,
name,
namespace,
markHubResourceAsInstalled,
markHubResourceAsInstalling,
version,
} = this.props
const { isTemplateInstalled } = this.state
if (getXoaPlan(+process.env.XOA_PLAN) === 'Community') {
subscribeAlert()
return
}
const resourceParams = await form({
render: props => (
<ResourceForm
install
multi
poolPredicate={isTemplateInstalled}
{...props}
/>
),
header: (
<span>
<Icon icon='add-vm' /> {name}
</span>
),
size: 'medium',
})
markHubResourceAsInstalling(id)
try {
await Promise.all(
resourceParams.pools.map(pool =>
downloadAndInstallResource({
namespace,
id,
version,
sr: pool.default_SR,
})
)
)
success(_('hubImportNotificationTitle'), _('successfulInstall'))
} catch (_error) {
error(_('hubImportNotificationTitle'), _error.message)
}
markHubResourceAsInstalled(id)
},
async create() {
const { isPoolCreated, installedTemplates } = this.state
const { name } = this.props
if (getXoaPlan(+process.env.XOA_PLAN) === 'Community') {
subscribeAlert()
return
}
const resourceParams = await form({
render: props => (
<ResourceForm poolPredicate={isPoolCreated} {...props} />
),
header: (
<span>
<Icon icon='add-vm' /> {name}
</span>
),
size: 'medium',
})
const { $pool } = resourceParams.pool
const template = find(installedTemplates, { $pool })
if (template !== undefined) {
this.props.router.push(
`/vms/new?pool=${$pool}&template=${template.id}`
)
} else {
throw new Error(`can't find template for pool: ${$pool}`)
}
},
async deleteTemplates(__, { name }) {
const { isPoolCreated } = this.state
const resourceParams = await form({
render: props => (
<ResourceForm
delete
multi
poolPredicate={isPoolCreated}
{...props}
/>
),
header: (
<span>
<Icon icon='vm-delete' /> {name}
</span>
),
size: 'medium',
})
const _templates = filter(this.state.installedTemplates, template =>
find(resourceParams.pools, { $pool: template.$pool })
)
await deleteTemplates(_templates)
},
updateSelectedInstallPools(_, selectedInstallPools) {
return {
selectedInstallPools,
}
},
updateSelectedCreatePool(_, selectedCreatePool) {
return {
selectedCreatePool,
}
},
redirectToTaskPage() {
this.props.router.push('/tasks')
},
},
computed: {
installedTemplates: (_, { id, templates }) =>
filter(templates, ['other.xo:resource:xva:id', id]),
isTemplateInstalledOnAllPools: ({ installedTemplates }, { pools }) =>
installedTemplates.length > 0 &&
pools.every(
pool =>
installedTemplates.find(template => template.$pool === pool.id) !==
undefined
),
isTemplateInstalled: ({ installedTemplates }) => pool =>
installedTemplates.find(template => template.$pool === pool.id) ===
undefined,
isPoolCreated: ({ installedTemplates }) => pool =>
installedTemplates.find(template => template.$pool === pool.id) !==
undefined,
},
}),
injectState,
({
effects,
hubInstallingResources,
id,
name,
os,
size,
state,
totalDiskSize,
version,
}) => (
<Card shadow>
<CardHeader>
{name}
<ActionButton
className='pull-right'
color='light'
data-name={name}
disabled={state.installedTemplates.length === 0}
handler={effects.deleteTemplates}
size='small'
style={{ border: 'none' }}
>
<Icon icon='delete' size='xs' />
</ActionButton>
<br />
</CardHeader>
<CardBlock className='text-center'>
<div>
<span className='text-muted'>{_('os')}</span> <strong>{os}</strong>
</div>
<div>
<span className='text-muted'>{_('version')}</span>
{' '}
<strong>{version}</strong>
</div>
<div>
<span className='text-muted'>{_('size')}</span>
{' '}
<strong>{formatSize(size)}</strong>
</div>
<div>
<span className='text-muted'>{_('totalDiskSize')}</span>
{' '}
<strong>{formatSize(totalDiskSize)}</strong>
</div>
<hr />
<Row>
<Col mediumSize={6}>
<ActionButton
block
disabled={state.isTemplateInstalledOnAllPools}
form={state.idInstallForm}
handler={effects.install}
icon='add'
pending={hubInstallingResources[id]}
>
{_('install')}
</ActionButton>
</Col>
<Col mediumSize={6}>
<ActionButton
block
disabled={state.installedTemplates.length === 0}
form={state.idCreateForm}
handler={effects.create}
icon='deploy'
>
{_('create')}
</ActionButton>
</Col>
</Row>
</CardBlock>
</Card>
),
])

View File

@@ -27,6 +27,7 @@ import BackupNg from './backup-ng'
import Dashboard from './dashboard'
import Home from './home'
import Host from './host'
import Hub from './hub'
import Jobs from './jobs'
import Menu from './menu'
import Modal, { alert, FormModal } from 'modal'
@@ -93,6 +94,7 @@ const BODY_STYLE = {
'vms/:id': Vm,
xoa: Xoa,
xosan: Xosan,
hub: Hub,
})
@connectStore(state => {
return {

View File

@@ -354,6 +354,11 @@ export default class Menu extends Component {
},
],
},
isAdmin && {
to: '/hub',
icon: 'menu-hub',
label: 'hubPage',
},
isAdmin && { to: '/about', icon: 'menu-about', label: 'aboutPage' },
!noOperatablePools && {
to: '/tasks',

View File

@@ -280,7 +280,12 @@ export default class NewVm extends BaseComponent {
}
componentDidMount() {
this._reset()
this._reset(() => {
const { template } = this.props
if (template !== undefined) {
this._initTemplate(this.props.template)
}
})
}
componentDidUpdate(prevProps) {
@@ -338,28 +343,31 @@ export default class NewVm extends BaseComponent {
// Actions ---------------------------------------------------------------------
_reset = () => {
this._replaceState({
bootAfterCreate: true,
CPUs: '',
cpuCap: '',
cpuWeight: '',
existingDisks: {},
fastClone: true,
hvmBootFirmware: '',
installMethod: 'noConfigDrive',
multipleVms: false,
name_label: '',
name_description: '',
nameLabels: map(Array(NB_VMS_MIN), (_, index) => `VM_${index + 1}`),
namePattern: '{name}%',
nbVms: NB_VMS_MIN,
VDIs: [],
VIFs: [],
seqStart: 1,
share: false,
tags: [],
})
_reset = callback => {
this._replaceState(
{
bootAfterCreate: true,
CPUs: '',
cpuCap: '',
cpuWeight: '',
existingDisks: {},
fastClone: true,
hvmBootFirmware: '',
installMethod: 'noConfigDrive',
multipleVms: false,
name_label: '',
name_description: '',
nameLabels: map(Array(NB_VMS_MIN), (_, index) => `VM_${index + 1}`),
namePattern: '{name}%',
nbVms: NB_VMS_MIN,
VDIs: [],
VIFs: [],
seqStart: 1,
share: false,
tags: [],
},
callback
)
}
_selfCreate = () => {