1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-23 22:50:09 +03:00

F #5422: Refactor api calls to VMs & Templates (#1991)

This commit is contained in:
Sergio Betanzos 2022-05-10 18:36:21 +02:00 committed by GitHub
parent e5e16ef9b5
commit 1de3f59297
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 641 additions and 236 deletions

View File

@ -40,7 +40,7 @@ const HeaderVmInfo = ({ id, type }) => {
const { push: redirectTo } = useHistory()
const { enqueueError } = useGeneralApi()
const { data: vm, isSuccess, isLoading, isError } = useGetVmQuery(id)
const { data: vm, isSuccess, isLoading, isError } = useGetVmQuery({ id })
const [getService, { data: serviceFlow }] = useLazyGetServiceQuery()
const ips = getIps(vm)

View File

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import { useMemo } from 'react'
import { useHistory } from 'react-router-dom'
import { Typography } from '@mui/material'
import {
AddSquare,
Import,
@ -32,40 +32,64 @@ import {
useUnlockTemplateMutation,
useCloneTemplateMutation,
useRemoveTemplateMutation,
useChangeTemplateOwnershipMutation,
useChangeTemplatePermissionsMutation,
} from 'client/features/OneApi/vmTemplate'
import { Tr, Translate } from 'client/components/HOC'
import { ChangeUserForm, ChangeGroupForm } from 'client/components/Forms/Vm'
import { CloneForm } from 'client/components/Forms/VmTemplate'
import { createActions } from 'client/components/Tables/Enhanced/Utils'
import { PATH } from 'client/apps/sunstone/routesOne'
import {
createActions,
GlobalAction,
} from 'client/components/Tables/Enhanced/Utils'
import { Tr, Translate } from 'client/components/HOC'
import { PATH } from 'client/apps/sunstone/routesOne'
import { T, VM_TEMPLATE_ACTIONS, RESOURCE_NAMES } from 'client/constants'
const MessageToConfirmAction = (rows) => {
const names = rows?.map?.(({ original }) => original?.NAME)
const ListVmTemplateNames = ({ rows = [] }) =>
rows?.map?.(({ id, original }) => {
const { ID, NAME } = original
return (
<>
<p>
<Translate word={T.VMTemplates} />
{`: ${names.join(', ')}`}
</p>
<p>
<Translate word={T.DoYouWantProceed} />
</p>
</>
)
}
return (
<Typography
key={`vm-template-${id}`}
variant="inherit"
component="span"
display="block"
>
{`#${ID} ${NAME}`}
</Typography>
)
})
const SubHeader = (rows) => <ListVmTemplateNames rows={rows} />
const MessageToConfirmAction = (rows, description) => (
<>
<ListVmTemplateNames rows={rows} />
{description && <Translate word={description} />}
<Translate word={T.DoYouWantProceed} />
</>
)
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
/**
* Generates the actions to operate resources on VM Template table.
*
* @returns {GlobalAction} - Actions
*/
const Actions = () => {
const history = useHistory()
const { view, getResourceView } = useViews()
const [lock] = useLockTemplateMutation()
const [unlock] = useUnlockTemplateMutation()
const [clone] = useCloneTemplateMutation()
const [remove] = useRemoveTemplateMutation()
const [changeOwnership] = useChangeTemplateOwnershipMutation()
const [changePermissions] = useChangeTemplatePermissionsMutation()
return useMemo(
() =>
@ -183,30 +207,72 @@ const Actions = () => {
{
accessor: VM_TEMPLATE_ACTIONS.CHANGE_OWNER,
name: T.ChangeOwner,
disabled: true,
isConfirmDialog: true,
onSubmit: () => undefined,
dialogProps: {
title: T.ChangeOwner,
subheader: SubHeader,
dataCy: `modal-${VM_TEMPLATE_ACTIONS.CHANGE_OWNER}`,
},
form: ChangeUserForm,
onSubmit: (rows) => (newOwnership) => {
rows?.map?.(({ original }) =>
changeOwnership({ id: original?.ID, ...newOwnership })
)
},
// onSubmit: (rows) => async (newOwnership) => {
// const ids = rows?.map?.(({ original }) => original?.ID)
// await Promise.all(
// ids.map((id) => changeOwnership({ id, ...newOwnership }))
// )
// },
},
{
accessor: VM_TEMPLATE_ACTIONS.CHANGE_GROUP,
name: T.ChangeGroup,
disabled: true,
isConfirmDialog: true,
onSubmit: () => undefined,
dialogProps: {
title: T.ChangeGroup,
subheader: SubHeader,
dataCy: `modal-${VM_TEMPLATE_ACTIONS.CHANGE_GROUP}`,
},
form: ChangeGroupForm,
onSubmit: (rows) => async (newOwnership) => {
const ids = rows?.map?.(({ original }) => original?.ID)
await Promise.all(
ids.map((id) => changeOwnership({ id, ...newOwnership }))
)
},
},
{
accessor: VM_TEMPLATE_ACTIONS.SHARE,
disabled: true,
name: T.Share,
isConfirmDialog: true,
onSubmit: () => undefined,
dialogProps: {
title: T.Share,
children: (rows) =>
MessageToConfirmAction(rows, T.ShareVmTemplateDescription),
},
onSubmit: (rows) => () => {
rows?.map?.(({ original }) =>
changePermissions({ id: original?.ID, groupUse: '1' })
)
},
},
{
accessor: VM_TEMPLATE_ACTIONS.UNSHARE,
disabled: true,
name: T.Unshare,
isConfirmDialog: true,
onSubmit: () => undefined,
dialogProps: {
title: T.Unshare,
children: (rows) =>
MessageToConfirmAction(
rows,
T.UnshareVmTemplateDescription
),
},
onSubmit: (rows) => () => {
rows?.map?.(({ original }) =>
changePermissions({ id: original?.ID, groupUse: '0' })
)
},
},
],
},
@ -239,7 +305,7 @@ const Actions = () => {
},
onSubmit: (rows) => async () => {
const ids = rows?.map?.(({ original }) => original?.ID)
await Promise.all(ids.map((id) => unlock(id)))
await Promise.all(ids.map((id) => unlock({ id })))
},
},
],

View File

@ -582,7 +582,7 @@ const Actions = () => {
},
onSubmit: (rows) => async () => {
const ids = rows?.map?.(({ original }) => original?.ID)
await Promise.all(ids.map((id) => unlock(id)))
await Promise.all(ids.map((id) => unlock({ id })))
},
},
],

View File

@ -33,7 +33,7 @@ import { getActionsAvailable, jsonToXml } from 'client/models/Helper'
*/
const VmCapacityTab = ({ tabProps: { actions } = {}, id }) => {
const [resizeCapacity] = useResizeMutation()
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const actionsAvailable = useMemo(() => {
const hypervisor = getHypervisor(vm)

View File

@ -30,7 +30,7 @@ import { T } from 'client/constants'
* @returns {ReactElement} Configuration tab
*/
const VmConfigurationTab = ({ id }) => {
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const { TEMPLATE, USER_TEMPLATE } = vm
return (

View File

@ -36,7 +36,7 @@ import { getActionsAvailable } from 'client/models/Helper'
* @returns {ReactElement} History tab
*/
const VmHistoryTab = ({ tabProps: { actions } = {}, id }) => {
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const [records, actionsAvailable] = useMemo(() => {
const hypervisor = getHypervisor(vm)

View File

@ -67,7 +67,7 @@ const VmInfoTab = ({ tabProps = {}, id }) => {
attributes_panel: attributesPanel,
} = tabProps
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const [changeVmOwnership] = useChangeVmOwnershipMutation()
const [changeVmPermissions] = useChangeVmPermissionsMutation()
const [updateUserTemplate] = useUpdateUserTemplateMutation()

View File

@ -47,7 +47,7 @@ const { ATTACH_NIC, DETACH_NIC, ATTACH_SEC_GROUP, DETACH_SEC_GROUP } =
* @returns {ReactElement} Networks tab
*/
const VmNetworkTab = ({ tabProps: { actions } = {}, id }) => {
const { data: vm } = useGetVmQuery(id)
const { data: vm } = useGetVmQuery({ id })
const [nics, hypervisor, actionsAvailable] = useMemo(() => {
const groupedNics = getNics(vm, {

View File

@ -59,7 +59,7 @@ const VmSchedulingTab = ({ tabProps: { actions } = {}, id }) => {
const [addScheduledAction] = useAddScheduledActionMutation()
const [updateScheduledAction] = useUpdateScheduledActionMutation()
const [deleteScheduledAction] = useDeleteScheduledActionMutation()
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const [scheduling, actionsAvailable] = useMemo(() => {
const hypervisor = getHypervisor(vm)

View File

@ -45,7 +45,7 @@ const { SNAPSHOT_CREATE, SNAPSHOT_REVERT, SNAPSHOT_DELETE } = VM_ACTIONS
* @returns {ReactElement} Snapshots tab
*/
const VmSnapshotTab = ({ tabProps: { actions } = {}, id }) => {
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const [snapshots, actionsAvailable] = useMemo(() => {
const hypervisor = getHypervisor(vm)

View File

@ -60,7 +60,7 @@ const {
* @returns {ReactElement} Storage tab
*/
const VmStorageTab = ({ tabProps: { actions } = {}, id }) => {
const { data: vm = {} } = useGetVmQuery(id)
const { data: vm = {} } = useGetVmQuery({ id })
const [disks, hypervisor, actionsAvailable] = useMemo(() => {
const hyperV = getHypervisor(vm)

View File

@ -46,9 +46,10 @@ const getTabComponent = (tabName) =>
const VmTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
const { isLoading, isError, error } = useGetVmQuery(id, {
refetchOnMountOrArgChange: 10,
})
const { isLoading, isError, error } = useGetVmQuery(
{ id },
{ refetchOnMountOrArgChange: 10 }
)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.VM

View File

@ -53,7 +53,7 @@ const Content = ({
key={`tab-${id ?? name}`}
data-cy={`tab-content-${id ?? name}`}
hidden={hidden}
border={addBorder}
border={addBorder ? 'true' : undefined}
>
<Fade in timeout={400}>
<TabContent sx={{ p: '1em .5em' }}>

View File

@ -741,6 +741,12 @@ module.exports = {
CloneWithImagesConcept: `
You can also clone any Image referenced inside this Template.
They will be cloned to a new Image, and made persistent`,
ShareVmTemplateDescription: `
The VM Template(s), along with any image referenced by it, will
be shared with the group's users. Permission changed: GROUP USE`,
UnshareVmTemplateDescription: `
The VM Template(s), along with any image referenced by it, will
be unshared with the group's users. Permission changed: GROUP USE`,
/* Virtual Network schema - network */
IP: 'IP',

View File

@ -0,0 +1,257 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2022, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Draft, ThunkAction } from '@reduxjs/toolkit'
import userApi from 'client/features/OneApi/user'
import groupApi from 'client/features/OneApi/group'
import { LockLevel, Permission, User, Group } from 'client/constants'
import { xmlToJson } from 'client/models/Helper'
/**
* Update the pool of resources with the new data.
*
* @param {string} params - The parameters from query
* @param {string} [params.id] - The id of the resource
* @param {string} [params.resourceFromQuery] - The resource from query (user, group, ...)
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updateResourceOnPool =
({ id: resourceId, resourceFromQuery }) =>
(draft) => {
if (resourceId !== undefined && Array.isArray(draft)) return
const index = draft.findIndex(({ ID }) => +ID === +resourceId)
index !== -1 && (draft[index] = resourceFromQuery)
}
/**
* Remove the resource from the pool.
*
* @param {string} params - The parameters from query
* @param {string} [params.id] - The id of the resource
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const removeResourceOnPool =
({ id: resourceId }) =>
(draft) => {
if (resourceId !== undefined && Array.isArray(draft)) return
draft.filter(({ ID }) => +ID !== +resourceId)
}
/**
* Update the name of a resource in the store.
*
* @param {string} params - The parameters from query
* @param {string} [params.id] - The id of the resource
* @param {string} [params.name] - The name of the resource
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updateNameOnResource =
({ id: resourceId, name: newName }) =>
(draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if ((updatePool && !resource) || newName !== undefined) return
resource.NAME = newName
}
/**
* Update the lock level of a resource in the store.
*
* @param {string} params - The parameters from query
* @param {string} [params.id] - The id of the resource
* @param {LockLevel} [params.level] - The new lock level
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updateLockLevelOnResource =
({ id: resourceId, level = '4' }) =>
(draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if (updatePool && !resource) return
resource.LOCK = { LOCKED: level }
}
/**
* Update to unlock a resource in the store.
*
* @param {string} params - The parameters from query
* @param {string} [params.id] - The id of the resource
* @param {string} [params.level] - The new lock level
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const removeLockLevelOnResource =
({ id: resourceId }) =>
(draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if (updatePool && !resource) return
resource.LOCK = undefined
}
/**
* Update the permissions of a resource in the store.
*
* @param {object} params - Request parameters
* @param {string} params.id - The id of the resource
* @param {Permission|'-1'} params.ownerUse - User use
* @param {Permission|'-1'} params.ownerManage - User manage
* @param {Permission|'-1'} params.ownerAdmin - User administrator
* @param {Permission|'-1'} params.groupUse - Group use
* @param {Permission|'-1'} params.groupManage - Group manage
* @param {Permission|'-1'} params.groupAdmin - Group administrator
* @param {Permission|'-1'} params.otherUse - Other use
* @param {Permission|'-1'} params.otherManage - Other manage
* @param {Permission|'-1'} params.otherAdmin - Other administrator
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updatePermissionOnResource =
({ id: resourceId, ...permissions }) =>
(draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if (updatePool && !resource) return
Object.entries(permissions)
.filter(([_, value]) => value !== '-1')
.forEach(([name, value]) => {
const ensuredName = {
ownerUse: 'OWNER_U',
ownerManage: 'OWNER_M',
ownerAdmin: 'OWNER_A',
groupUse: 'GROUP_U',
groupManage: 'GROUP_M',
groupAdmin: 'GROUP_A',
otherUse: 'OTHER_U',
otherManage: 'OTHER_M',
otherAdmin: 'OTHER_A',
}[name]
resource.PERMISSIONS[ensuredName] = value
})
}
/**
* Select the users and groups from the current state.
* - If `options.userId` is provided, only the user with the given id will be selected.
* - If `options.groupId` is provided, only the group with the given id will be selected.
*
* @param {object} state - The current state
* @param {string} options - The options to filter the users and groups
* @param {string} options.userId - The user id
* @param {string} options.groupId - The group id
* @returns {
* { users: User[], groups: Group[] } | { user: User, group: Group }
* } - The users and groups or the user and group by id
*/
export const selectOwnershipFromState = (state, { userId, groupId } = {}) => {
const { data: users } = userApi.endpoints.getUsers.select()(state)
const { data: groups } = groupApi.endpoints.getGroups.select()(state)
if (!userId && !groupId) return { users, groups }
const user = users.find(({ ID }) => +ID === +userId)
const group = groups.find(({ ID }) => +ID === +groupId)
return { user, group }
}
/**
* Update the ownership of a resource in the store.
*
* @param {object} state - The current state
* @param {string} [params] - The parameters from query
* @param {string} [params.id] - The id of the resource
* @param {string} [params.user] - The user id to update
* @param {string} [params.group] - The group id to update
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updateOwnershipOnResource = (
state,
{ id: resourceId, user: userId, group: groupId } = {}
) => {
const { user, group } = selectOwnershipFromState(state, { userId, groupId })
return (draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if (updatePool && !resource) return
user?.ID > -1 && (resource.UID = user.ID)
user?.NAME !== undefined && (resource.UNAME = user.NAME)
group?.ID > -1 && (resource.GID = group.ID)
group?.NAME !== undefined && (resource.GNAME = group.NAME)
}
}
/**
* Update the template or user template of a resource in the store.
*
* @param {object} params - Request params
* @param {number|string} params.id - The id of the resource
* @param {string} params.template - The new user template contents on XML format
* @param {0|1} params.replace
* - Update type:
* ``0``: Replace the whole template.
* ``1``: Merge new template with the existing one.
* @param {string} [userTemplateAttribute] - The attribute name of the user template. By default is `USER_TEMPLATE`.
* @returns {function(Draft):ThunkAction} - Dispatches the action
*/
export const updateUserTemplateOnResource =
(
{ id: resourceId, template: xml, replace = 0 },
userTemplateAttribute = 'USER_TEMPLATE'
) =>
(draft) => {
const updatePool = resourceId !== undefined && Array.isArray(draft)
const newTemplateJson = xmlToJson(xml)
const resource = updatePool
? draft.find(({ ID }) => +ID === +resourceId)
: draft
if (updatePool && !resource) return
resource[userTemplateAttribute] =
+replace === 0
? newTemplateJson
: { ...resource[userTemplateAttribute], ...newTemplateJson }
}

View File

@ -80,7 +80,7 @@ const getResourceFromEventState = (data) => {
const UpdateFromSocket =
({ updateQueryData, resource }) =>
async (
id,
{ id },
{ cacheEntryRemoved, cacheDataLoaded, updateCachedData, getState, dispatch }
) => {
const { zone } = getState().general

View File

@ -18,15 +18,25 @@ import {
Actions as ExtraActions,
Commands as ExtraCommands,
} from 'server/routes/api/vm/routes'
import {
oneApi,
ONE_RESOURCES,
ONE_RESOURCES_POOL,
} from 'client/features/OneApi'
import {
updateResourceOnPool,
removeResourceOnPool,
updateNameOnResource,
updateLockLevelOnResource,
removeLockLevelOnResource,
updatePermissionOnResource,
updateOwnershipOnResource,
updateUserTemplateOnResource,
} from 'client/features/OneApi/common'
import { actions as guacamoleActions } from 'client/features/Guacamole/slice'
import { UpdateFromSocket } from 'client/features/OneApi/socket'
import http from 'client/utils/rest'
import { xmlToJson } from 'client/models/Helper'
import {
LockLevel,
FilterFlag,
@ -44,7 +54,7 @@ const vmApi = oneApi.injectEndpoints({
* Retrieves information for all or part of
* the VMs in the pool.
*
* @param {object} params - Request params
* @param {object} params - Request parameters
* @param {boolean} params.extended - Retrieves information for all or part
* @param {FilterFlag} [params.filter] - Filter flag
* @param {number} [params.start] - Range start ID
@ -85,32 +95,37 @@ const vmApi = oneApi.injectEndpoints({
/**
* Retrieves information for the virtual machine.
*
* @param {string} id - VM id
* @param {object} params - Request parameters
* @param {string} params.id - VM id
* @returns {VmType} Get VM identified by id
* @throws Fails when response isn't code 200
*/
query: (id) => {
query: (params) => {
const name = Actions.VM_INFO
const command = { name, ...Commands[name] }
return { params: { id }, command }
return { params, command }
},
transformResponse: (data) => data?.VM ?? {},
providesTags: (_, __, id) => [{ type: VM, id }],
async onQueryStarted(id, { dispatch, queryFulfilled }) {
try {
const { data: queryVm } = await queryFulfilled
const { data: resourceFromQuery } = await queryFulfilled
dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) => {
const index = draft.findIndex(({ ID }) => +ID === +id)
index !== -1 && (draft[index] = queryVm)
})
vmApi.util.updateQueryData(
'getVms',
undefined,
updateResourceOnPool({ id, resourceFromQuery })
)
)
} catch {
// if the query fails, we want to remove the resource from the pool
dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) =>
draft.filter(({ ID }) => +ID !== +id)
vmApi.util.updateQueryData(
'getVms',
undefined,
removeResourceOnPool({ id })
)
)
}
@ -590,7 +605,7 @@ const vmApi = oneApi.injectEndpoints({
* If set any permission to -1, it's not changed.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {Permission|'-1'} params.ownerUse - User use
* @param {Permission|'-1'} params.ownerManage - User manage
* @param {Permission|'-1'} params.ownerAdmin - User administrator
@ -610,33 +625,18 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
async onQueryStarted(
{ id, ...permissions },
{ dispatch, queryFulfilled }
) {
const patchResult = dispatch(
vmApi.util.updateQueryData('getVm', id, (draft) => {
Object.entries(permissions)
.filter(([_, value]) => value !== '-1')
.forEach(([name, value]) => {
const ensuredName = {
ownerUse: 'OWNER_U',
ownerManage: 'OWNER_M',
ownerAdmin: 'OWNER_A',
groupUse: 'GROUP_U',
groupManage: 'GROUP_M',
groupAdmin: 'GROUP_A',
otherUse: 'OTHER_U',
otherManage: 'OTHER_M',
otherAdmin: 'OTHER_A',
}[name]
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
const patchVm = dispatch(
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
updatePermissionOnResource(params)
)
)
draft.PERMISSIONS[ensuredName] = value
})
})
)
queryFulfilled.catch(patchResult.undo)
queryFulfilled.catch(patchVm.undo)
} catch {}
},
}),
changeVmOwnership: builder.mutation({
@ -644,7 +644,7 @@ const vmApi = oneApi.injectEndpoints({
* Changes the ownership bits of a virtual machine.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {number} params.user - The user id
* @param {number} params.group - The group id
* @returns {number} Virtual machine id
@ -657,13 +657,26 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
async onQueryStarted(params, { getState, dispatch, queryFulfilled }) {
try {
const patchVm = dispatch(
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
updateOwnershipOnResource(getState(), params)
)
)
queryFulfilled.catch(patchVm.undo())
} catch {}
},
}),
renameVm: builder.mutation({
/**
* Renames a virtual machine.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {string} params.name - The new name
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
@ -675,16 +688,28 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
async onQueryStarted({ id, name }, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) => {
const vm = draft.find(({ ID }) => +ID === +id)
vm && (vm.NAME = name)
})
const patchVm = dispatch(
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
updateNameOnResource(params)
)
)
const patchVms = dispatch(
vmApi.util.updateQueryData(
'getVms',
undefined,
updateNameOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVm.undo()
patchVms.undo()
})
} catch {}
},
}),
@ -693,7 +718,7 @@ const vmApi = oneApi.injectEndpoints({
* Creates a new virtual machine snapshot.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {string} params.name - The new snapshot name
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
@ -711,8 +736,8 @@ const vmApi = oneApi.injectEndpoints({
* Reverts a virtual machine to a snapshot.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string|number} params.snapshot - The snapshot id
* @param {string} params.id - Virtual machine id
* @param {string} params.snapshot - The snapshot id
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
*/
@ -729,8 +754,8 @@ const vmApi = oneApi.injectEndpoints({
* Deletes a virtual machine snapshot.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string|number} params.snapshot - The snapshot id
* @param {string} params.id - Virtual machine id
* @param {string} params.snapshot - The snapshot id
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
*/
@ -747,7 +772,7 @@ const vmApi = oneApi.injectEndpoints({
* Changes the capacity of the virtual machine.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {string} params.template - Template containing the new capacity
* @param {boolean} params.enforce - `true` to enforce the Host capacity isn't over committed
* @returns {number} Virtual machine id
@ -766,7 +791,7 @@ const vmApi = oneApi.injectEndpoints({
* Replaces the user template contents.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {string} params.template - The new user template contents on syntax XML
* @param {0|1} params.replace
* - Update type:
@ -782,33 +807,22 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
async onQueryStarted(
{ id, template: xml, replace = 0 },
{ dispatch, queryFulfilled }
) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
// update user template by id
const patchVm = dispatch(
vmApi.util.updateQueryData('getVm', id, (draft) => {
draft.USER_TEMPLATE =
+replace === 0
? xmlToJson(xml)
: { ...draft.USER_TEMPLATE, ...xmlToJson(xml) }
})
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
updateUserTemplateOnResource(params)
)
)
// update user template on pool by id (if exists)
const patchVms = dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) => {
const vm = draft.find(({ ID }) => +ID === +id)
if (!vm) return
vm.USER_TEMPLATE =
+replace === 0
? xmlToJson(xml)
: { ...vm.USER_TEMPLATE, ...xmlToJson(xml) }
})
vmApi.util.updateQueryData(
'getVms',
undefined,
updateUserTemplateOnResource(params)
)
)
queryFulfilled.catch(() => {
@ -823,7 +837,7 @@ const vmApi = oneApi.injectEndpoints({
* Updates (appends) a set of supported configuration attributes in the VM template.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {string} params.template - The new configuration contents on syntax XML
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
@ -845,7 +859,7 @@ const vmApi = oneApi.injectEndpoints({
* if the operation was successful or not.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {0|1|2|3|4} params.operation - Recover operation:
* failure (0), success (1), retry (2), delete (3), delete-recreate (4)
* @returns {number} Virtual machine id
@ -864,7 +878,7 @@ const vmApi = oneApi.injectEndpoints({
* Locks a Virtual Machine. Lock certain actions depending on blocking level.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - Virtual machine id
* @param {string} params.id - Virtual machine id
* @param {LockLevel} params.level - Lock level
* @param {boolean} params.test - Checks if the object is already locked to return an error
* @returns {number} Virtual machine id
@ -877,16 +891,28 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
async onQueryStarted({ id, level = '4' }, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) => {
const vm = draft.find(({ ID }) => +ID === +id)
vm && (vm.LOCK = { LOCKED: level })
})
const patchVm = dispatch(
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
updateLockLevelOnResource(params)
)
)
const patchVms = dispatch(
vmApi.util.updateQueryData(
'getVms',
undefined,
updateLockLevelOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVm.undo()
patchVms.undo()
})
} catch {}
},
}),
@ -894,27 +920,40 @@ const vmApi = oneApi.injectEndpoints({
/**
* Unlocks a Virtual Machine.
*
* @param {string|number} id - Virtual machine id
* @param {object} params - Request parameters
* @param {string} params.id - Virtual machine id
* @returns {number} Virtual machine id
* @throws Fails when response isn't code 200
*/
query: (id) => {
query: (params) => {
const name = Actions.VM_UNLOCK
const command = { name, ...Commands[name] }
return { params: { id }, command }
return { params, command }
},
invalidatesTags: (_, __, id) => [{ type: VM, id }],
async onQueryStarted(id, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
dispatch(
vmApi.util.updateQueryData('getVms', undefined, (draft) => {
const vm = draft.find(({ ID }) => +ID === +id)
vm && (vm.LOCK = undefined)
})
const patchVm = dispatch(
vmApi.util.updateQueryData(
'getVm',
{ id: params.id },
removeLockLevelOnResource(params)
)
)
const patchVms = dispatch(
vmApi.util.updateQueryData(
'getVms',
undefined,
removeLockLevelOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVm.undo()
patchVms.undo()
})
} catch {}
},
}),

View File

@ -14,13 +14,23 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/utils/constants/commands/template'
import {
oneApi,
ONE_RESOURCES,
ONE_RESOURCES_POOL,
} from 'client/features/OneApi'
import {
updateResourceOnPool,
removeResourceOnPool,
updateNameOnResource,
updateLockLevelOnResource,
removeLockLevelOnResource,
updatePermissionOnResource,
updateOwnershipOnResource,
updateUserTemplateOnResource,
} from 'client/features/OneApi/common'
import { LockLevel, FilterFlag, Permission, VmTemplate } from 'client/constants'
import { xmlToJson } from 'client/models/Helper'
const { TEMPLATE } = ONE_RESOURCES
const { TEMPLATE_POOL, VM_POOL } = ONE_RESOURCES_POOL
@ -49,7 +59,10 @@ const vmTemplateApi = oneApi.injectEndpoints({
providesTags: (vmTemplates) =>
vmTemplates
? [
...vmTemplates.map(({ ID }) => ({ type: TEMPLATE_POOL, ID })),
...vmTemplates.map(({ ID }) => ({
type: TEMPLATE_POOL,
id: `${ID}`,
})),
TEMPLATE_POOL,
]
: [TEMPLATE_POOL],
@ -75,19 +88,25 @@ const vmTemplateApi = oneApi.injectEndpoints({
providesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted({ id }, { dispatch, queryFulfilled }) {
try {
const { data: queryTemplate } = await queryFulfilled
const { data: resourceFromQuery } = await queryFulfilled
dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
(draft) => {
const index = draft.findIndex(({ ID }) => +ID === +id)
index !== -1 && (draft[index] = queryTemplate)
}
updateResourceOnPool({ id, resourceFromQuery })
)
)
} catch {}
} catch {
// if the query fails, we want to remove the resource from the pool
dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
removeResourceOnPool({ id })
)
)
}
},
}),
allocateTemplate: builder.mutation({
@ -188,36 +207,21 @@ const vmTemplateApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted(
{ id, template: xml, replace = 0 },
{ dispatch, queryFulfilled }
) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
// update template by id
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData('getTemplate', id, (draft) => {
draft.TEMPLATE =
+replace === 0
? xmlToJson(xml)
: { ...draft.TEMPLATE, ...xmlToJson(xml) }
})
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
updateUserTemplateOnResource(params, 'TEMPLATE')
)
)
// update template on pool by id (if exists)
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
(draft) => {
const template = draft.find(({ ID }) => +ID === +id)
if (!template) return
template.TEMPLATE =
+replace === 0
? xmlToJson(xml)
: { ...template.TEMPLATE, ...xmlToJson(xml) }
}
updateUserTemplateOnResource(params, 'TEMPLATE')
)
)
@ -234,7 +238,7 @@ const vmTemplateApi = oneApi.injectEndpoints({
* If set any permission to -1, it's not changed.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - VM Template id
* @param {string} params.id - VM Template id
* @param {Permission|'-1'} params.ownerUse - User use
* @param {Permission|'-1'} params.ownerManage - User manage
* @param {Permission|'-1'} params.ownerAdmin - User administrator
@ -255,33 +259,29 @@ const vmTemplateApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted(
{ id, ...permissions },
{ dispatch, queryFulfilled }
) {
const patchResult = dispatch(
vmTemplateApi.util.updateQueryData('getTemplate', { id }, (draft) => {
Object.entries(permissions)
.filter(([_, value]) => value !== '-1')
.forEach(([name, value]) => {
const ensuredName = {
ownerUse: 'OWNER_U',
ownerManage: 'OWNER_M',
ownerAdmin: 'OWNER_A',
groupUse: 'GROUP_U',
groupManage: 'GROUP_M',
groupAdmin: 'GROUP_A',
otherUse: 'OTHER_U',
otherManage: 'OTHER_M',
otherAdmin: 'OTHER_A',
}[name]
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
updatePermissionOnResource(params)
)
)
draft.PERMISSIONS[ensuredName] = value
})
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
updatePermissionOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVmTemplate.undo()
patchVmTemplates.undo()
})
)
queryFulfilled.catch(patchResult.undo)
} catch {}
},
}),
changeTemplateOwnership: builder.mutation({
@ -303,18 +303,29 @@ const vmTemplateApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted(
{ id, user, group },
{ dispatch, queryFulfilled, getState }
) {
const patchResult = dispatch(
vmTemplateApi.util.updateQueryData('getTemplate', id, (draft) => {
user > 0 && (draft.UID = user)
group > 0 && (draft.GID = group)
})
)
async onQueryStarted(params, { getState, dispatch, queryFulfilled }) {
try {
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
updateOwnershipOnResource(getState(), params)
)
)
queryFulfilled.catch(patchResult.undo)
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
updateOwnershipOnResource(getState(), params)
)
)
queryFulfilled.catch(() => {
patchVmTemplate.undo()
patchVmTemplates.undo()
})
} catch {}
},
}),
renameTemplate: builder.mutation({
@ -334,20 +345,28 @@ const vmTemplateApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted({ id, name }, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
updateNameOnResource(params)
)
)
dispatch(
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
(draft) => {
const template = draft.find(({ ID }) => +ID === +id)
template && (template.NAME = name)
}
updateNameOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVmTemplate.undo()
patchVmTemplates.undo()
})
} catch {}
},
}),
@ -356,7 +375,7 @@ const vmTemplateApi = oneApi.injectEndpoints({
* Locks a VM Template.
*
* @param {object} params - Request parameters
* @param {string|number} params.id - VM Template id
* @param {string} params.id - VM Template id
* @param {LockLevel} params.lock - Lock level
* @param {boolean} params.test - Checks if the object is already locked to return an error
* @returns {number} VM Template id
@ -369,20 +388,28 @@ const vmTemplateApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: TEMPLATE, id }],
async onQueryStarted({ id, level = '4' }, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
updateLockLevelOnResource(params)
)
)
dispatch(
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
(draft) => {
const template = draft.find(({ ID }) => +ID === +id)
template && (template.LOCK = { LOCKED: level })
}
updateLockLevelOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVmTemplate.undo()
patchVmTemplates.undo()
})
} catch {}
},
}),
@ -390,31 +417,40 @@ const vmTemplateApi = oneApi.injectEndpoints({
/**
* Unlocks a VM Template.
*
* @param {string|number} id - VM Template id
* @param {object} params - Request parameters
* @param {string} params.id - VM Template id
* @returns {number} VM Template id
* @throws Fails when response isn't code 200
*/
query: (id) => {
query: (params) => {
const name = Actions.TEMPLATE_UNLOCK
const command = { name, ...Commands[name] }
return { params: { id }, command }
return { params, command }
},
invalidatesTags: (_, __, id) => [{ type: TEMPLATE, id }],
async onQueryStarted(id, { dispatch, queryFulfilled }) {
async onQueryStarted(params, { dispatch, queryFulfilled }) {
try {
await queryFulfilled
const patchVmTemplate = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplate',
{ id: params.id },
removeLockLevelOnResource(params)
)
)
dispatch(
const patchVmTemplates = dispatch(
vmTemplateApi.util.updateQueryData(
'getTemplates',
undefined,
(draft) => {
const template = draft.find(({ ID }) => +ID === +id)
template && (template.LOCK = undefined)
}
removeLockLevelOnResource(params)
)
)
queryFulfilled.catch(() => {
patchVmTemplate.undo()
patchVmTemplates.undo()
})
} catch {}
},
}),