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

F OpenNebula/one#5422: Add resource types

This commit is contained in:
Sergio Betanzos 2021-06-22 18:26:23 +02:00
parent 515dbc8612
commit 59c16d7522
No known key found for this signature in database
GPG Key ID: E3E704F097737136
32 changed files with 153 additions and 119 deletions

View File

@ -27,7 +27,7 @@ export const TIME_HIDE_LOGO = 1500
export const _APPS = defaultApps
export const APPS = Object.keys(defaultApps)
export const APP_URL = defaultAppName ? `/${defaultAppName}` : ''
export const WEBSOCKET_URL = `${APP_URL}/websockets/provision`
export const WEBSOCKET_URL = `${APP_URL}/websockets`
export const STATIC_FILES_URL = `${APP_URL}/client/assets`
export const IMAGES_URL = `${STATIC_FILES_URL}/images`

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { applicationService } from 'client/features/One/application/services'
import { RESOURCES } from 'client/features/One/slice'
export const getApplication = createAction('cluster', applicationService.getApplication)
export const getApplications = createAction(
'application/pool',
applicationService.getApplications,
response => ({ applications: response })
response => ({ [RESOURCES.document[100]]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/application/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useApplication = () => (
useSelector(state => state.one.applications)
useSelector(state => state.one[RESOURCES.document[100]])
)
export const useApplicationApi = () => {

View File

@ -1,5 +1,6 @@
import { createAction } from 'client/features/One/utils'
import { applicationTemplateService } from 'client/features/One/applicationTemplate/services'
import { RESOURCES } from 'client/features/One/slice'
export const getApplicationTemplate = createAction(
'application-template',
@ -9,7 +10,7 @@ export const getApplicationTemplate = createAction(
export const getApplicationsTemplates = createAction(
'application-template/pool',
applicationTemplateService.getApplicationsTemplates,
response => ({ applicationsTemplates: response })
response => ({ [RESOURCES.document[101]]: response })
)
export const createApplicationTemplate = createAction(

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/applicationTemplate/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useApplicationTemplate = () => (
useSelector(state => state.one.applicationsTemplates)
useSelector(state => state.one[RESOURCES.document[101]])
)
export const useApplicationTemplateApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { clusterService } from 'client/features/One/cluster/services'
import { RESOURCES } from 'client/features/One/slice'
export const getCluster = createAction('cluster', clusterService.getCluster)
export const getClusters = createAction(
'cluster/pool',
clusterService.getClusters,
response => ({ clusters: response })
response => ({ [RESOURCES.cluster]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/cluster/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useCluster = () => (
useSelector(state => state.one.clusters)
useSelector(state => state.one[RESOURCES.cluster])
)
export const useClusterApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { datastoreService } from 'client/features/One/datastore/services'
import { RESOURCES } from 'client/features/One/slice'
export const getDatastore = createAction('datastore', datastoreService.getDatastore)
export const getDatastores = createAction(
'datastore/pool',
datastoreService.getDatastores,
response => ({ datastores: response })
response => ({ [RESOURCES.datastore]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/datastore/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useDatastore = () => (
useSelector(state => state.one.datastores)
useSelector(state => state.one[RESOURCES.datastore])
)
export const useDatastoreApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { groupService } from 'client/features/One/group/services'
import { RESOURCES } from 'client/features/One/slice'
export const getGroup = createAction('group', groupService.getGroup)
export const getGroups = createAction(
'group/pool',
groupService.getGroups,
response => ({ groups: response })
response => ({ [RESOURCES.group]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/group/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useGroup = () => (
useSelector(state => state.one.groups)
useSelector(state => state.one[RESOURCES.group])
)
export const useGroupApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { hostService } from 'client/features/One/host/services'
import { RESOURCES } from 'client/features/One/slice'
export const getHost = createAction('host', hostService.getHost)
export const getHosts = createAction(
'host/pool',
hostService.getHosts,
response => ({ hosts: response })
response => ({ [RESOURCES.host]: response })
)

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { imageService } from 'client/features/One/image/services'
import { RESOURCES } from 'client/features/One/slice'
export const getImage = createAction('image', imageService.getImage)
export const getImages = createAction(
'image/pool',
imageService.getImages,
response => ({ images: response })
response => ({ [RESOURCES.image]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/image/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useImage = () => (
useSelector(state => state.one.images)
useSelector(state => state.one[RESOURCES.image])
)
export const useImageApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { marketplaceService } from 'client/features/One/marketplace/services'
import { RESOURCES } from 'client/features/One/slice'
export const getMarketplace = createAction('marketplace', marketplaceService.getMarketplace)
export const getMarketplaces = createAction(
'marketplace/pool',
marketplaceService.getMarketplaces,
response => ({ marketplaces: response })
response => ({ [RESOURCES.marketplace]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/marketplace/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useMarketplace = () => (
useSelector(state => state.one.marketplaces)
useSelector(state => state.one.[RESOURCES.marketplace])
)
export const useMarketplaceApi = () => {

View File

@ -1,5 +1,6 @@
import { createAction } from 'client/features/One/utils'
import { marketplaceAppService } from 'client/features/One/marketplaceApp/services'
import { RESOURCES } from 'client/features/One/slice'
export const getMarketplaceApp = createAction(
'app',
@ -9,5 +10,5 @@ export const getMarketplaceApp = createAction(
export const getMarketplaceApps = createAction(
'app/pool',
marketplaceAppService.getMarketplaceApps,
response => ({ apps: response })
response => ({ [RESOURCES.app]: response })
)

View File

@ -1,12 +1,13 @@
import { createAction } from 'client/features/One/utils'
import { providerService } from 'client/features/One/provider/services'
import { RESOURCES } from 'client/features/One/slice'
export const getProvider = createAction('provider', providerService.getProvider)
export const getProviders = createAction(
'provider/pool',
providerService.getProviders,
res => ({ providers: res })
res => ({ [RESOURCES.document[102]]: res })
)
export const getProviderConnection = createAction('provider', providerService.getProviderConnection)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/provider/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useProvider = () => (
useSelector(state => state.one.providers)
useSelector(state => state.one[RESOURCES.document[102]])
)
export const useProviderApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { provisionService } from 'client/features/One/provision/services'
import { RESOURCES } from 'client/features/One/slice'
export const getProvisionsTemplates = createAction(
'provisions-template/pool',
provisionService.getProvisionsTemplates,
res => ({ provisionsTemplates: res })
res => ({ [RESOURCES.document.defaults]: res })
)
export const createProvisionTemplate = createAction(
@ -17,7 +18,7 @@ export const getProvision = createAction('provision', provisionService.getProvis
export const getProvisions = createAction(
'provision/pool',
provisionService.getProvisions,
res => ({ provisions: res })
res => ({ [RESOURCES.document[103]]: res })
)
export const createProvision = createAction('provision/create', provisionService.createProvision)

View File

@ -3,13 +3,14 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/provision/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useProvisionTemplate = () => (
useSelector(state => state.one.provisionsTemplates)
useSelector(state => state.one[RESOURCES.document.defaults])
)
export const useProvision = () => (
useSelector(state => state.one.provisions)
useSelector(state => state.one[RESOURCES.document[103]])
)
export const useProvisionApi = () => {

View File

@ -1,34 +1,67 @@
import { createSlice } from '@reduxjs/toolkit'
import { createSlice, isFulfilled } from '@reduxjs/toolkit'
import { socketEventState } from 'client/features/One/socket/actions'
import { updateResourceList } from 'client/features/One/utils'
import { eventUpdateResourceState } from 'client/features/One/socket/actions'
const getNameListFromType = type => RESOURCES[type.split('/')[0]]
const ATTRIBUTES_EDITABLE = ['NAME', 'STATE', 'LCM_STATE']
const RESOURCES = {
acl: 'acl',
app: 'apps',
cluster: 'clusters',
datastore: 'datastores',
file: 'files',
group: 'groups',
host: 'hosts',
image: 'images',
marketplace: 'marketplaces',
secgroups: 'securityGroups',
template: 'templates',
user: 'users',
vdc: 'vdc',
vm: 'vms',
vmgroup: 'vmGroups',
vn: 'vNetworks',
vntemplate: 'vNetworksTemplates',
zone: 'zones',
document: {
100: 'applications',
101: 'applicationsTemplates',
102: 'providers',
103: 'provisions',
// extra: only for client
defaults: 'provisionsTemplates'
}
}
const initial = {
requests: {},
acl: [],
applications: [],
applicationsTemplates: [],
apps: [],
clusters: [],
datastores: [],
files: [],
groups: [],
hosts: [],
images: [],
marketplaces: [],
providers: [],
provisions: [],
provisionsTemplates: [],
securityGroups: [],
templates: [],
users: [],
vdc: [],
virtualRouters: [],
vmGroups: [],
vms: [],
vNetworks: [],
vNetworksTemplates: [],
zones: []
[RESOURCES.acl]: [],
[RESOURCES.app]: [],
[RESOURCES.cluster]: [],
[RESOURCES.datastore]: [],
[RESOURCES.file]: [],
[RESOURCES.group]: [],
[RESOURCES.host]: [],
[RESOURCES.image]: [],
[RESOURCES.marketplace]: [],
[RESOURCES.secgroups]: [],
[RESOURCES.template]: [],
[RESOURCES.user]: [],
[RESOURCES.vdc]: [],
[RESOURCES.vm]: [],
[RESOURCES.vmgroup]: [],
[RESOURCES.vn]: [],
[RESOURCES.vntemplate]: [],
[RESOURCES.zone]: [],
[RESOURCES.document[100]]: [],
[RESOURCES.document[101]]: [],
[RESOURCES.document[102]]: [],
[RESOURCES.document[103]]: [],
[RESOURCES.document.defaults]: []
}
const { actions, reducer } = createSlice({
@ -37,9 +70,17 @@ const { actions, reducer } = createSlice({
extraReducers: builder => {
builder
.addCase('logout', () => initial)
.addCase(
socketEventState.fulfilled,
(state, { payload }) => ({ ...state, ...payload })
.addMatcher(
({ type }) =>
type.endsWith('/fulfilled') &&
(type.includes(eventUpdateResourceState.typePrefix) || type.includes('/detail')),
(state, { payload, type }) => {
// TYPE and DATA can be force with the payload
const name = getNameListFromType(payload?.type ?? type)
const newList = updateResourceList(state[name], payload?.data ?? payload)
return { ...state, [name]: newList }
}
)
.addMatcher(
({ type }) => type.includes('/pool') && type.endsWith('/pending'),
@ -54,7 +95,7 @@ const { actions, reducer } = createSlice({
.addMatcher(
({ type }) => type.includes('/pool') && type.endsWith('/fulfilled'),
(state, { payload, type }) => {
const { [type.replace('/fulfilled', '')]: _, ...requests } = state.requests
const { [getNameListFromType(type)]: _, ...requests } = state.requests
return { ...state, requests, ...payload }
}
@ -62,4 +103,4 @@ const { actions, reducer } = createSlice({
}
})
export { actions, reducer }
export { actions, reducer, RESOURCES, ATTRIBUTES_EDITABLE }

View File

@ -1,6 +1,7 @@
import { createAsyncThunk } from '@reduxjs/toolkit'
import * as actions from 'client/features/General/actions'
import { RESOURCES } from 'client/features/One/slice'
import { generateKey } from 'client/utils'
const MESSAGE_PROVISION_SUCCESS_CREATED = 'Provision successfully created'
@ -11,34 +12,13 @@ const COMMANDS = {
delete: 'delete'
}
const RESOURCES = {
acl: 'acl',
app: 'apps',
cluster: 'clusters',
datastore: 'datastores',
file: 'files',
group: 'groups',
host: 'hosts',
image: 'images',
marketplace: 'marketplaces',
secgroups: 'securityGroups',
template: 'templates',
user: 'users',
vdc: 'vdc',
vm: 'vms',
vmgroup: 'vmGroups',
vn: 'vNetworks',
vntemplate: 'vNetworksTemplates',
zone: 'zones',
document: {
100: 'applications',
101: 'applicationsTemplates',
102: 'providers',
103: 'provisions'
}
export const getResourceFromEventState = socketData => {
const { HOOK_OBJECT: name, [name]: value } = socketData?.data?.HOOK_MESSAGE ?? {}
return { name: String(name).toLowerCase(), value }
}
const getResourceFromEventApi = (eventApi = {}) => {
export const getResourceFromEventApi = (eventApi = {}) => {
const { CALL: command = '', CALL_INFO: info = {} } = eventApi?.HOOK_MESSAGE
const { EXTRA: extra, RESULT: result, PARAMETERS } = info
@ -79,40 +59,25 @@ export const socketEventApi = createAsyncThunk(
}
)
export const socketEventState = createAsyncThunk(
export const eventUpdateResourceState = createAsyncThunk(
'socket/event-state',
({ data }, { getState }) => {
// possible hook objects: VM, IMAGE, HOST
const { RESOURCE_ID, HOOK_OBJECT: name, [name]: value } = data?.HOOK_MESSAGE
socketData => {
const { name, value } = getResourceFromEventState(socketData)
// update the list if event returns resource value
if (!value || value === '') return
const { NAME, STATE, LCM_STATE } = value
// this won't be a document resource never
const resource = RESOURCES[String(name).toLowerCase()]
const currentList = getState()?.one?.[resource] ?? []
const exists = currentList.some(({ ID }) => ID === RESOURCE_ID)
// update if exists in current list, if not add it
const updatedList = exists
? currentList?.map(item => ({
...item,
...(item?.ID === RESOURCE_ID && {
NAME,
STATE,
...(item?.LCM_STATE && { LCM_STATE })
})
}))
: [value, ...currentList]
return { [resource]: updatedList }
return { type: name, data: value }
},
{
condition: payload => payload?.data?.HOOK_MESSAGE?.HOOK_TYPE === 'STATE'
condition: socketData => {
const { name, value } = getResourceFromEventState(socketData)
return (
socketData?.data?.HOOK_MESSAGE?.HOOK_TYPE === 'STATE' &&
// possible hook objects: VM, IMAGE, HOST
['vm', 'host', 'image'].includes(name) &&
// update the list if event returns resource value
value !== ''
)
}
}
)

View File

@ -1,5 +1,6 @@
import { createAction } from 'client/features/One/utils'
import { userService } from 'client/features/One/user/services'
import { RESOURCES } from 'client/features/One/slice'
export const changeGroup = createAction('user/change-group', userService.changeGroup)
export const getUser = createAction('user', userService.getUser)
@ -7,7 +8,7 @@ export const getUser = createAction('user', userService.getUser)
export const getUsers = createAction(
'user/pool',
userService.getUsers,
response => ({ users: response })
response => ({ [RESOURCES.user]: response })
)
export const updateUser = createAction('user/update', userService.updateUser)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/user/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useUser = () => (
useSelector(state => state.one.users)
useSelector(state => state.one[RESOURCES.user])
)
export const useUserApi = () => {

View File

@ -2,7 +2,7 @@ import { createAction } from 'client/features/One/utils'
import { vmService } from 'client/features/One/vm/services'
import { filterBy } from 'client/utils'
export const getVm = createAction('vm', vmService.getVm)
export const getVm = createAction('vm/detail', vmService.getVm)
export const getVms = createAction(
'vm/pool',

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { vmTemplateService } from 'client/features/One/vmTemplate/services'
import { RESOURCES } from 'client/features/One/slice'
export const getVmTemplate = createAction('vm-template', vmTemplateService.getVmTemplate)
export const getVmTemplates = createAction(
'vm-template/pool',
vmTemplateService.getVmTemplates,
response => ({ templates: response })
response => ({ [RESOURCES.template]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/vmTemplate/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useVmTemplate = () => (
useSelector(state => state.one.templates)
useSelector(state => state.one[RESOURCES.template])
)
export const useVmTemplateApi = () => {

View File

@ -1,10 +1,11 @@
import { createAction } from 'client/features/One/utils'
import { vNetworkService } from 'client/features/One/vnetwork/services'
import { RESOURCES } from 'client/features/One/slice'
export const getVNetwork = createAction('vnet', vNetworkService.getVNetwork)
export const getVNetworks = createAction(
'vnet/pool',
vNetworkService.getVNetworks,
response => ({ vNetworks: response })
response => ({ [RESOURCES.vn]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/vnetwork/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useVNetwork = () => (
useSelector(state => state.one.vNetworks)
useSelector(state => state.one.[RESOURCES.vn])
)
export const useVNetworkApi = () => {

View File

@ -1,5 +1,6 @@
import { createAction } from 'client/features/One/utils'
import { vNetworkTemplateService } from 'client/features/One/vnetworkTemplate/services'
import { RESOURCES } from 'client/features/One/slice'
export const getVNetworkTemplate = createAction(
'vnet-template',
@ -9,5 +10,5 @@ export const getVNetworkTemplate = createAction(
export const getVNetworksTemplates = createAction(
'vnet-template/pool',
vNetworkTemplateService.getVNetworksTemplates,
response => ({ vNetworksTemplates: response })
response => ({ [RESOURCES.vntemplate]: response })
)

View File

@ -3,9 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import * as actions from 'client/features/One/vnetworkTemplate/actions'
import { RESOURCES } from 'client/features/One/slice'
export const useVNetworkTemplate = () => (
useSelector(state => state.one.vNetworksTemplates)
useSelector(state => state.one.[RESOURCES.vntemplate])
)
export const useVNetworkTemplateApi = () => {