From 1f003cbcd51158c33697bdf4efc641f1ec036210 Mon Sep 17 00:00:00 2001 From: vichansson Date: Mon, 14 Oct 2024 18:15:40 +0300 Subject: [PATCH] B OpenNebula/one#6744: Fix placement expression formatting (#3258) Signed-off-by: Victor Hansson (cherry picked from commit 60bfce4146bb6a701a47d99d12143d904ebd670e) --- .../FormControl/ToggleController.js | 12 +++ .../ExtraConfiguration/placement/schema.js | 74 ++++++++++++------- .../VmTemplate/CreateForm/Steps/index.js | 32 +++++++- .../src/client/constants/translates.js | 4 +- 4 files changed, 94 insertions(+), 28 deletions(-) diff --git a/src/fireedge/src/client/components/FormControl/ToggleController.js b/src/fireedge/src/client/components/FormControl/ToggleController.js index 57efe68ae7..f57db0e398 100644 --- a/src/fireedge/src/client/components/FormControl/ToggleController.js +++ b/src/fireedge/src/client/components/FormControl/ToggleController.js @@ -51,6 +51,7 @@ const ToggleController = memo( notNull = false, readOnly = false, onConditionChange, + defaultValue, }) => { const { field: { ref, value: optionSelected, onChange, onBlur }, @@ -76,6 +77,16 @@ const ToggleController = memo( [onChange, onConditionChange, readOnly, notNull] ) + // Safe loading of default value + useEffect(() => { + if ( + defaultValue && + values?.some(({ text, value }) => [text, value]?.includes(defaultValue)) + ) { + onChange(defaultValue) + } + }, []) + return ( {label && ( @@ -127,6 +138,7 @@ ToggleController.propTypes = { notNull: PropTypes.bool, readOnly: PropTypes.bool, onConditionChange: PropTypes.func, + defaultValue: PropTypes.any, } ToggleController.displayName = 'ToggleController' diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js index ce6d5a2237..1d5ba1a09d 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/placement/schema.js @@ -34,15 +34,12 @@ import { transformXmlString } from 'client/models/Helper' const addHypervisorRequirement = (schedRequirements, hypervisor) => { // Regular expression pattern to match (HYPERVISOR=VALUE) - const regexPattern = /\(HYPERVISOR=(kvm|dummy|lxc|qemu)\)/ + const regexPattern = /HYPERVISOR=(kvm|dummy|lxc|qemu)/ // If exists a condition with hypervisor, replace the type. If not, add the hypervisor type. if (regexPattern.test(schedRequirements)) { // Replace the matched pattern with the new hypervisor - return schedRequirements.replace( - regexPattern, - '(HYPERVISOR=' + hypervisor + ')' - ) + return schedRequirements.replace(regexPattern, 'HYPERVISOR=' + hypervisor) } else { // Add the condition only if (!hypervisor) { @@ -50,8 +47,8 @@ const addHypervisorRequirement = (schedRequirements, hypervisor) => { } return schedRequirements - ? `(${schedRequirements}) & (HYPERVISOR=${hypervisor})` - : `(HYPERVISOR=${hypervisor})` + ? `${schedRequirements} | HYPERVISOR=${hypervisor}` + : `HYPERVISOR=${hypervisor}` } } @@ -63,21 +60,26 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ type: INPUT_TYPES.TEXT, dependOf: [ '$general.HYPERVISOR', - '$extra.CLUSTER_HOST_TABLE', + '$extra.PLACEMENT_CLUSTER_TABLE', + '$extra.PLACEMENT_HOST_TABLE', '$extra.CLUSTER_HOST_TYPE', ], watcher: (dependencies, { formContext }) => { - const [hypervisor, clusterHostTable, clusterHostType] = dependencies + const [hypervisor, clusterTable, hostTable, clusterHostType] = dependencies + + const clusterHostTable = clusterHostType?.includes(T.Cluster) + ? clusterTable + : hostTable if (!hypervisor) { return } - const tableType = clusterHostType?.includes(T.Cluster) ? 'CLUSTER' : 'HOST' - const regexPattern = new RegExp(`\\b${tableType}_ID\\s*=\\s*(\\d+)`) + const tableType = clusterHostType?.includes(T.Cluster) ? 'CLUSTER_' : '' + const regexPattern = new RegExp(`\\b${tableType}ID\\s*=\\s*\\d+`) const actualValue = formContext.getValues('extra.SCHED_REQUIREMENTS') - const parts = actualValue?.split('&')?.map((part) => part?.trim()) + const parts = actualValue?.split('|')?.map((part) => part?.trim()) const matchedParts = parts?.filter((part) => regexPattern.test(part)) const nonMatchedParts = parts?.filter((part) => !regexPattern.test(part)) @@ -98,7 +100,7 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ const newExpressions = clusterHostTable ?.filter((id) => !remainingIDs.includes(id)) - .map((id) => `(${tableType}_ID = ${id})`) + .map((id) => `${tableType}ID = ${id}`) const updatedParts = [ ...(nonMatchedParts ?? []), @@ -106,11 +108,11 @@ const HOST_REQ_FIELD = (isUpdate, modifiedFields, instantiate) => ({ ...(newExpressions ?? []), ] - const updatedValue = updatedParts?.join(' & ') ?? '' + const updatedValue = updatedParts?.join(' | ') ?? '' // Check if the hypervisor condition already exists in the actualValue const hasHypervisorCondition = actualValue?.includes( - `(HYPERVISOR=${hypervisor})` + `HYPERVISOR=${hypervisor}` ) // Add the hypervisor condition only if it doesn't exist @@ -223,26 +225,47 @@ const DS_RANK_FIELD = { const TABLE_TYPE = { name: 'CLUSTER_HOST_TYPE', type: INPUT_TYPES.TOGGLE, - values: () => - arrayToOptions([T.SelectCluster, T.SelectHost], { - addEmpty: false, - }), + values: arrayToOptions([T.SelectCluster, T.SelectHost], { + addEmpty: false, + }), validation: string() .trim() .required() - .default(() => T.Host), + .default(() => T.SelectCluster), notNull: true, + defaultValue: T.SelectCluster, grid: { xs: 12, md: 12 }, } /** @type {Field} Cluster selection field */ -const CLUSTER_HOST_TABLE = { - name: 'CLUSTER_HOST_TABLE', +const CLUSTER_TABLE = { + name: 'PLACEMENT_CLUSTER_TABLE', dependOf: 'CLUSTER_HOST_TYPE', type: INPUT_TYPES.TABLE, - Table: (tableType) => - tableType?.includes(T.Cluster) ? ClustersTable : HostsTable, + Table: ClustersTable, + htmlType: (tableType) => + !tableType?.includes(T.Cluster) && INPUT_TYPES.HIDDEN, singleSelect: false, + fieldProps: { + preserveState: true, + }, + validation: array(string().trim()) + .required() + .default(() => undefined), + grid: { xs: 12, md: 12 }, +} + +/** @type {Field} Cluster selection field */ +const HOST_TABLE = { + name: 'PLACEMENT_HOST_TABLE', + dependOf: 'CLUSTER_HOST_TYPE', + type: INPUT_TYPES.TABLE, + Table: HostsTable, + htmlType: (tableType) => !tableType?.includes(T.Host) && INPUT_TYPES.HIDDEN, + singleSelect: false, + fieldProps: { + preserveState: true, + }, validation: array(string().trim()) .required() .default(() => undefined), @@ -269,7 +292,8 @@ const SECTIONS = (oneConfig, adminGroup, isUpdate, modifiedFields) => [ fields: disableFields( [ TABLE_TYPE, - CLUSTER_HOST_TABLE, + CLUSTER_TABLE, + HOST_TABLE, HOST_REQ_FIELD(isUpdate, modifiedFields), HOST_POLICY_TYPE_FIELD, HOST_RANK_FIELD, diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js index 52e3095765..ce8d508ce8 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/index.js @@ -25,6 +25,7 @@ import General, { STEP_ID as GENERAL_ID, } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General' +import { T } from 'client/constants' import { userInputsToArray } from 'client/models/Helper' import { createSteps, getUnknownAttributes, decodeBase64 } from 'client/utils' @@ -95,8 +96,37 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], { } } + // Init placement + const schedRequirements = vmTemplate?.TEMPLATE?.SCHED_REQUIREMENTS + if (schedRequirements) { + objectSchema[EXTRA_ID].SCHED_REQUIREMENTS = schedRequirements + const parts = schedRequirements?.split(' | ') + const tableIds = parts?.reduce((ids, part) => { + if (part?.includes('ID')) { + const isCluster = part.toUpperCase().includes(T.Cluster.toUpperCase()) + const tableId = isCluster ? T.Cluster : T.Host + const partId = part?.split(' = ')?.at(-1)?.trim() + if (!partId) return ids + ;(ids[tableId] ??= []).push(partId) + } + + return ids + }, {}) + + if (tableIds?.[T.Cluster]) { + objectSchema[EXTRA_ID].PLACEMENT_CLUSTER_TABLE = tableIds[T.Cluster] + } + + if (tableIds?.[T.Host]) { + objectSchema[EXTRA_ID].PLACEMENT_HOST_TABLE = tableIds[T.Host] + } + } + + const defaultType = T.SelectCluster + objectSchema[EXTRA_ID].CLUSTER_HOST_TYPE = defaultType + const knownTemplate = schema.cast(objectSchema, { - stripUnknown: true, + stripUnknown: false, context: { ...vmTemplate, [EXTRA_ID]: vmTemplate.TEMPLATE }, }) diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index 649e250896..0bc2c3698b 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -197,8 +197,8 @@ module.exports = { SelectDisk: 'Select disk', SelectDockerHubTag: 'Select DockerHub image tag (default latest)', SelectGroup: 'Select a group', - SelectHost: 'Select host', - SelectHosts: 'Select hosts', + SelectHost: 'Select Host', + SelectHosts: 'Select Hosts', SelectMarketplace: 'Select Marketplace', SelectNetwork: 'Select a network', SelectVirtualNetworks: 'Select virtual networks',