Skip to content

Commit

Permalink
feat(new-vm): self service with resource sets
Browse files Browse the repository at this point in the history
Fixes #1155
  • Loading branch information
Pierre Donias authored and olivierlambert committed Jul 13, 2016
1 parent 539d136 commit 0d47332
Show file tree
Hide file tree
Showing 11 changed files with 618 additions and 188 deletions.
3 changes: 2 additions & 1 deletion src/common/intl/locales/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ export default {
alarmPool: 'Pool',
alarmRemoveAll: 'Supprimer toutes les alarmes',
// ----- New VM -----
newVmCreateNewVmOn: 'Créer une nouvelle VM sur {pool}',
newVmCreateNewVmOn: 'Créer une nouvelle VM sur {select}',
newVmCreateNewVmOn2: 'Créer une nouvelle VM sur {select1} ou {select2}',
newVmInfoPanel: 'Informations',
newVmNameLabel: 'Nom',
newVmTemplateLabel: 'Modèle',
Expand Down
10 changes: 9 additions & 1 deletion src/common/intl/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ var messages = {
selectPifs: 'Select PIF(s)…',
selectPools: 'Select Pool(s)…',
selectRemotes: 'Select Remote(s)…',
selectResourceSets: 'Select resource set(s)…',
selectResourceSetsVmTemplate: 'Select template(s)…',
selectResourceSetsSr: 'Select SR(s)…',
selectResourceSetsNetwork: 'Select network(s)…',
selectResourceSetsVdi: 'Select disk(s)…',
selectSrs: 'Select SR(s)…',
selectVms: 'Select VM(s)…',
selectVmTemplates: 'Select VM template(s)…',
Expand Down Expand Up @@ -583,7 +588,9 @@ var messages = {
alarmRemoveAll: 'Remove all alarms',

// ----- New VM -----
newVmCreateNewVmOn: 'Create a new VM on {pool}',
newVmCreateNewVmOn: 'Create a new VM on {select}',
newVmCreateNewVmOn2: 'Create a new VM on {select1} or {select2}',
newVmCreateNewVmNoPermission: 'You have no permission to create a VM',
newVmInfoPanel: 'Infos',
newVmNameLabel: 'Name',
newVmTemplateLabel: 'Template',
Expand Down Expand Up @@ -622,6 +629,7 @@ var messages = {
newVmCreateVms: 'Create VMs',
newVmCreateVmsConfirm: 'Are you sure you want to create {nbVms} VMs?',
newVmMultipleVms: 'Multiple VMs:',
newVmSelectResourceSet: 'Select a resource set:',

// ----- Self -----
resourceSets: 'Resource sets',
Expand Down
5 changes: 5 additions & 0 deletions src/common/render-xo-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ const xoItemToRender = {
<Icon icon='user' /> {user.email}
</span>
),
resourceSet: resourceSet => (
<span>
<Icon icon='resource-set' /> {resourceSet.name}
</span>
),

// XO objects.
pool: pool => (
Expand Down
190 changes: 188 additions & 2 deletions src/common/select-objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import React from 'react'
import assign from 'lodash/assign'
import classNames from 'classnames'
import filter from 'lodash/filter'
import flatten from 'lodash/flatten'
import forEach from 'lodash/forEach'
import groupBy from 'lodash/groupBy'
import keyBy from 'lodash/keyBy'
import keys from 'lodash/keys'
import map from 'lodash/map'
import sortBy from 'lodash/sortBy'
import store from 'store'
import { parse as parseRemote } from 'xo-remote-parser'

import _ from './intl'
Expand All @@ -19,16 +21,19 @@ import {
createFilter,
createGetObjectsOfType,
createGetTags,
createSelector
createSelector,
getObject
} from './selectors'
import {
connectStore,
mapPlus
mapPlus,
resolveResourceSets
} from './utils'
import {
isSrWritable,
subscribeGroups,
subscribeRemotes,
subscribeResourceSets,
subscribeRoles,
subscribeUsers
} from './xo'
Expand Down Expand Up @@ -580,3 +585,184 @@ export const SelectRemote = makeSubscriptionSelect(subscriber => {

return unsubscribeRemotes
}, { placeholder: _('selectRemotes') })

// ===================================================================

export const SelectResourceSet = makeSubscriptionSelect(subscriber => {
const unsubscribeResourceSets = subscribeResourceSets(resourceSets => {
const xoObjects = map(sortBy(resolveResourceSets(resourceSets), 'name'), resourceSet => ({...resourceSet, type: 'resourceSet'}))

subscriber({xoObjects})
})

return unsubscribeResourceSets
}, { placeholder: _('selectResourceSets') })

// ===================================================================

export class SelectResourceSetsVmTemplate extends Component {
get value () {
return this.refs.select.value
}

set value (value) {
this.refs.select.value = value
}

componentWillMount () {
this.componentWillUnmount = subscribeResourceSets(resourceSets => {
this.setState({
resourceSets: resolveResourceSets(resourceSets)
})
})
}

_getTemplates = createSelector(
() => this.props.resourceSet,
({ objectsByType }) => {
const { predicate } = this.props
const templates = objectsByType['VM-template']
return sortBy(predicate ? filter(templates, predicate) : templates, 'name_label')
}
)

render () {
return (
<GenericSelect
ref='select'
placeholder={_('selectResourceSetsVmTemplate')}
{...this.props}
xoObjects={this._getTemplates()}
/>
)
}
}

// ===================================================================

export class SelectResourceSetsSr extends Component {
get value () {
return this.refs.select.value
}

set value (value) {
this.refs.select.value = value
}

componentWillMount () {
this.componentWillUnmount = subscribeResourceSets(resourceSets => {
this.setState({
resourceSets: resolveResourceSets(resourceSets)
})
})
}

_getSrs = createSelector(
() => this.props.resourceSet,
({ objectsByType }) => {
const { predicate } = this.props
const srs = objectsByType['SR']
return sortBy(predicate ? filter(srs, predicate) : srs, 'name_label')
}
)

render () {
return (
<GenericSelect
ref='select'
placeholder={_('selectResourceSetsSr')}
{...this.props}
xoObjects={this._getSrs()}
/>
)
}
}

// ===================================================================

export class SelectResourceSetsVdi extends Component {
get value () {
return this.refs.select.value
}

set value (value) {
this.refs.select.value = value
}

componentWillMount () {
this.componentWillUnmount = subscribeResourceSets(resourceSets => {
this.setState({
resourceSets: resolveResourceSets(resourceSets)
})
})
}

_getObject (id) {
return getObject(store.getState(), id, true)
}

_getSrs = createSelector(
() => this.props.resourceSet,
({ objectsByType }) => {
const { srPredicate } = this.props
const srs = objectsByType['SR']
return srPredicate ? filter(srs, srPredicate) : srs
}
)

_getVdis = createSelector(
this._getSrs,
srs => sortBy(map(flatten(map(srs, sr => sr.VDIs)), this._getObject), 'name_label')
)

render () {
return (
<GenericSelect
ref='select'
placeholder={_('selectResourceSetsVdi')}
{...this.props}
xoObjects={this._getVdis()}
/>
)
}
}

// ===================================================================

export class SelectResourceSetsNetwork extends Component {
get value () {
return this.refs.select.value
}

set value (value) {
this.refs.select.value = value
}

componentWillMount () {
this.componentWillUnmount = subscribeResourceSets(resourceSets => {
this.setState({
resourceSets: resolveResourceSets(resourceSets)
})
})
}

_getNetworks = createSelector(
() => this.props.resourceSet,
({ objectsByType }) => {
const { predicate } = this.props
const networks = objectsByType['network']
return sortBy(predicate ? filter(networks, predicate) : networks, 'name_label')
}
)

render () {
return (
<GenericSelect
ref='select'
placeholder={_('selectResourceSetsNetwork')}
{...this.props}
xoObjects={this._getNetworks()}
/>
)
}
}
7 changes: 6 additions & 1 deletion src/common/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,18 @@ const _getPermissionsPredicate = invoke(() => {

// Creates an object selector from an id selector.
export const createGetObject = (idSelector = _getId) =>
(state, props) => {
(state, props, useResourceSet) => {
const object = state.objects.all[idSelector(state, props)]
if (!object) {
return
}

if (useResourceSet) {
return object
}

const predicate = _getPermissionsPredicate(state)

if (!predicate) {
if (predicate == null) {
return object // no filtering
Expand Down
37 changes: 37 additions & 0 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import React from 'react'
import replace from 'lodash/replace'
import store from 'store'
import { connect } from 'react-redux'
import { getObject } from 'selectors'

import BaseComponent from './base-component'
import invoke from './invoke'
Expand Down Expand Up @@ -355,6 +357,41 @@ export function rethrow (cb) {
)
}

// ===================================================================

export const resolveResourceSets = resourceSets => (
map(resourceSets, resourceSet => {
const { objects, ...attrs } = resourceSet
const resolvedObjects = {}
const resolvedSet = {
...attrs,
missingObjects: [],
objectsByType: resolvedObjects
}
const state = store.getState()

forEach(objects, id => {
const object = getObject(state, id, true) // true: useResourceSet to bypass permissions

// Error, missing resource.
if (!object) {
resolvedSet.missingObjects.push(id)
return
}

const { type } = object

if (!resolvedObjects[type]) {
resolvedObjects[type] = [ object ]
} else {
resolvedObjects[type].push(object)
}
})

return resolvedSet
})
)

// -------------------------------------------------------------------

// Creates a string replacer based on a pattern and a list of rules
Expand Down

0 comments on commit 0d47332

Please sign in to comment.