feat(vm.set): move a vm in to/out of a Self Service Group (#649)

See vatesfr/xo-web#1913
This commit is contained in:
badrAZ 2018-01-31 18:37:29 +01:00 committed by Julien Fontanet
parent 2b4a7c40e1
commit bf9b53c4d9
4 changed files with 72 additions and 27 deletions

View File

@ -1,5 +1,4 @@
import concat from 'lodash/concat' import concat from 'lodash/concat'
import merge from 'lodash/merge'
import { format } from 'json-rpc-peer' import { format } from 'json-rpc-peer'
import { ignoreErrors } from 'promise-toolbox' import { ignoreErrors } from 'promise-toolbox'
import { import {
@ -360,15 +359,7 @@ async function delete_ ({
// Update resource sets // Update resource sets
const resourceSet = xapi.xo.getData(vm._xapiId, 'resourceSet') const resourceSet = xapi.xo.getData(vm._xapiId, 'resourceSet')
if (resourceSet != null) { if (resourceSet != null) {
const resourceSetUsage = this.computeVmResourcesUsage(vm) this.setVmResourceSet(vm._xapiId, null)::ignoreErrors()
const ipPoolsUsage = await this.computeVmIpPoolsUsage(vm)
ignoreErrors.call(
this.releaseLimitsInResourceSet(
merge(resourceSetUsage, ipPoolsUsage),
resourceSet
)
)
} }
return xapi.deleteVm(vm._xapiId, deleteDisks, force) return xapi.deleteVm(vm._xapiId, deleteDisks, force)
@ -504,11 +495,22 @@ migrate.resolve = {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
export function set (params) { export async function set (params) {
const VM = extract(params, 'VM') const VM = extract(params, 'VM')
const xapi = this.getXapi(VM) const xapi = this.getXapi(VM)
const vmId = VM._xapiId
return xapi.editVm(VM._xapiId, params, async (limits, vm) => { const resourceSetId = extract(params, 'resourceSet')
if (resourceSetId !== undefined) {
if (this.user.permission !== 'admin') {
throw unauthorized()
}
await this.setVmResourceSet(vmId, resourceSetId)
}
return xapi.editVm(vmId, params, async (limits, vm) => {
const resourceSet = xapi.xo.getData(vm, 'resourceSet') const resourceSet = xapi.xo.getData(vm, 'resourceSet')
if (resourceSet) { if (resourceSet) {
@ -576,6 +578,9 @@ set.params = {
videoram: { type: ['string', 'number'], optional: true }, videoram: { type: ['string', 'number'], optional: true },
coresPerSocket: { type: ['string', 'number', 'null'], optional: true }, coresPerSocket: { type: ['string', 'number', 'null'], optional: true },
// Move the vm In to/Out of Self Service
resourceSet: { type: ['string', 'null'], optional: true },
} }
set.resolve = { set.resolve = {

View File

@ -1,4 +1,5 @@
import checkAuthorization from 'xo-acl-resolver' import checkAuthorization from 'xo-acl-resolver'
import { forEach, includes, map } from 'lodash'
import { import {
ModelAlreadyExists, ModelAlreadyExists,
@ -8,9 +9,6 @@ import {
} from '../models/acl' } from '../models/acl'
import { import {
createRawObject, createRawObject,
forEach,
includes,
mapToArray,
} from '../utils' } from '../utils'
// =================================================================== // ===================================================================
@ -59,7 +57,7 @@ export default class {
push.apply(acls, entries) push.apply(acls, entries)
})(acls.push) })(acls.push)
await Promise.all(mapToArray( await Promise.all(map(
subjects, subjects,
subject => this.getAclsForSubject(subject).then(pushAcls) subject => this.getAclsForSubject(subject).then(pushAcls)
)) ))
@ -133,6 +131,11 @@ export default class {
) )
} }
async removeAclsForObject (objectId) {
const acls = this._acls
await acls.remove(map(await acls.get({ object: objectId }), 'id'))
}
// ----------------------------------------------------------------- // -----------------------------------------------------------------
async _getPermissionsByRole () { async _getPermissionsByRole () {

View File

@ -1,20 +1,24 @@
import every from 'lodash/every'
import keyBy from 'lodash/keyBy'
import remove from 'lodash/remove'
import some from 'lodash/some'
import synchronized from 'decorator-synchronized' import synchronized from 'decorator-synchronized'
import {
assign,
every,
forEach,
isObject,
keyBy,
map as mapToArray,
remove,
some,
} from 'lodash'
import { import {
noSuchObject, noSuchObject,
unauthorized, unauthorized,
} from 'xo-common/api-errors' } from 'xo-common/api-errors'
import { import {
forEach, asyncMap,
generateUnsecureToken, generateUnsecureToken,
isObject,
lightSet, lightSet,
map, map,
mapToArray,
streamToArray, streamToArray,
} from '../utils' } from '../utils'
@ -124,9 +128,12 @@ export default class {
} }
} }
computeVmResourcesUsage (vm) { async computeVmResourcesUsage (vm) {
return computeVmResourcesUsage( return assign(
this._xo.getXapi(vm).getObject(vm._xapiId) computeVmResourcesUsage(
this._xo.getXapi(vm).getObject(vm._xapiId)
),
await this._xo.computeVmIpPoolsUsage(vm)
) )
} }
@ -344,4 +351,34 @@ export default class {
await Promise.all(mapToArray(sets, set => this._save(set))) await Promise.all(mapToArray(sets, set => this._save(set)))
} }
async setVmResourceSet (vmId, resourceSetId) {
const xapi = this._xo.getXapi(vmId)
const previousResourceSetId = xapi.xo.getData(vmId, 'resourceSet')
if (resourceSetId === previousResourceSetId || (previousResourceSetId === undefined && resourceSetId === null)) {
return
}
const resourcesUsage = await this.computeVmResourcesUsage(this._xo.getObject(vmId))
if (resourceSetId != null) {
await this.allocateLimitsInResourceSet(resourcesUsage, resourceSetId)
}
if (previousResourceSetId !== undefined) {
await this.releaseLimitsInResourceSet(resourcesUsage, previousResourceSetId)
}
await xapi.xo.setData(vmId, 'resourceSet', resourceSetId === undefined ? null : resourceSetId)
if (previousResourceSetId !== undefined) {
await this._xo.removeAclsForObject(vmId)
}
if (resourceSetId != null) {
const { subjects } = await this.getResourceSet(resourceSetId)
await asyncMap(subjects, subject =>
this._xo.addAcl(subject, vmId, 'admin')
)
}
}
} }

View File

@ -311,7 +311,7 @@ export default class {
await xapi._updateObjectMapProperty( await xapi._updateObjectMapProperty(
xapi.getObject(id), xapi.getObject(id),
'other_config', 'other_config',
{ [`xo:${camelToSnakeCase(key)}`]: JSON.stringify(value) } { [`xo:${camelToSnakeCase(key)}`]: value !== null ? JSON.stringify(value) : value }
) )
// Register the updated object. // Register the updated object.