From d59f30c382dc5db19445044d0a9f57adacef805c Mon Sep 17 00:00:00 2001 From: Jorge Miguel Lobo Escalona Date: Fri, 22 Sep 2023 12:15:52 +0200 Subject: [PATCH] F #1636: Add CPU_MODEL in VM Template (#2751) --- .../booting/cpuModelSchema.js | 81 +++++++++++++++++++ .../ExtraConfiguration/booting/schema.js | 30 ++++--- .../VmTemplate/CreateForm/Steps/index.js | 50 ++++++++---- .../src/client/constants/translates.js | 1 + src/fireedge/src/client/models/Host.js | 16 ++++ .../form-panels/create/wizard-tabs/os.js | 45 ++++++++++- .../create/wizard-tabs/os/html.hbs | 7 +- 7 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/cpuModelSchema.js diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/cpuModelSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/cpuModelSchema.js new file mode 100644 index 00000000000..4132b832dad --- /dev/null +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/cpuModelSchema.js @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2023, 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. * + * ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2023, 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 { array, string } from 'yup' + +import { + DEFAULT_CPU_MODELS, + HYPERVISORS, + INPUT_TYPES, + T, +} from 'client/constants' +import { useGetHostsQuery } from 'client/features/OneApi/host' +import { getKvmCpuFeatures, getKvmCpuModels } from 'client/models/Host' +import { Field, arrayToOptions } from 'client/utils' + +const { vcenter, firecracker, lxc } = HYPERVISORS + +/** @type {Field} CPU model field */ +export const MODEL = { + name: 'CPU_MODEL.MODEL', + label: T.CpuModel, + notOnHypervisors: [vcenter, firecracker, lxc], + type: INPUT_TYPES.SELECT, + values: () => { + const { data: hosts = [] } = useGetHostsQuery() + const kvmCpuModels = getKvmCpuModels(hosts) + kvmCpuModels.unshift(...DEFAULT_CPU_MODELS) + + return arrayToOptions(kvmCpuModels) + }, + validation: string() + .trim() + .notRequired() + .default(() => undefined), +} + +/** @type {Field} Features field */ +export const FEATURES = { + name: 'CPU_MODEL.FEATURES', + label: T.CpuFeature, + notOnHypervisors: [vcenter, firecracker, lxc], + type: INPUT_TYPES.SELECT, + multiple: true, + values: () => { + const { data: hosts = [] } = useGetHostsQuery() + const kvmFeatures = getKvmCpuFeatures(hosts) + + return arrayToOptions(kvmFeatures) + }, + validation: array(string().trim()).default(() => []), +} + +/** @type {Field[]} List of CPU_MODEL fields */ +export const CPU_MODEL_FIELDS = [MODEL, FEATURES] diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js index 30a617b9875..eb1ba943f7f 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/schema.js @@ -17,19 +17,20 @@ import { BaseSchema, string } from 'yup' import { BOOT_FIELDS } from './bootSchema' +import { CPU_MODEL_FIELDS } from './cpuModelSchema' +import { FEATURES_FIELDS } from './featuresSchema' import { KERNEL_FIELDS } from './kernelSchema' import { RAMDISK_FIELDS } from './ramdiskSchema' -import { FEATURES_FIELDS } from './featuresSchema' import { RAW_FIELDS } from './rawSchema' +import { HYPERVISORS, T } from 'client/constants' import { Field, Section, - getObjectSchemaFromFields, - filterFieldsByHypervisor, disableFields, + filterFieldsByHypervisor, + getObjectSchemaFromFields, } from 'client/utils' -import { T, HYPERVISORS } from 'client/constants' /** * @param {HYPERVISORS} [hypervisor] - Template hypervisor @@ -39,10 +40,10 @@ import { T, HYPERVISORS } from 'client/constants' */ const SECTIONS = (hypervisor, oneConfig, adminGroup) => [ { - id: 'os-boot', - legend: T.Boot, + id: 'os-cpu-model', + legend: T.CpuModel, fields: disableFields( - filterFieldsByHypervisor(BOOT_FIELDS, hypervisor), + filterFieldsByHypervisor(CPU_MODEL_FIELDS, hypervisor), 'OS', oneConfig, adminGroup @@ -78,6 +79,16 @@ const SECTIONS = (hypervisor, oneConfig, adminGroup) => [ adminGroup ), }, + { + id: 'os-boot', + legend: T.Boot, + fields: disableFields( + filterFieldsByHypervisor(BOOT_FIELDS, hypervisor), + 'OS', + oneConfig, + adminGroup + ), + }, { id: 'os-raw', legend: T.RawData, @@ -117,9 +128,10 @@ const FIELDS = (hypervisor) => [ */ const SCHEMA = (hypervisor) => getObjectSchemaFromFields(FIELDS(hypervisor)) -export { SECTIONS, FIELDS, BOOT_ORDER_FIELD, SCHEMA } export * from './bootSchema' +export * from './cpuModelSchema' +export * from './featuresSchema' export * from './kernelSchema' export * from './ramdiskSchema' -export * from './featuresSchema' export * from './rawSchema' +export { BOOT_ORDER_FIELD, FIELDS, SCHEMA, SECTIONS } 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 74a55eabfd1..e6dd5fd0b94 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 @@ -15,25 +15,25 @@ * ------------------------------------------------------------------------- */ import { reach } from 'yup' -import General, { - STEP_ID as GENERAL_ID, -} from 'client/components/Forms/VmTemplate/CreateForm/Steps/General' -import ExtraConfiguration, { - STEP_ID as EXTRA_ID, -} from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration' import CustomVariables, { STEP_ID as CUSTOM_ID, } from 'client/components/Forms/VmTemplate/CreateForm/Steps/CustomVariables' +import ExtraConfiguration, { + STEP_ID as EXTRA_ID, +} from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration' +import General, { + STEP_ID as GENERAL_ID, +} from 'client/components/Forms/VmTemplate/CreateForm/Steps/General' +import { MEMORY_RESIZE_OPTIONS, T } from 'client/constants' import { jsonToXml, userInputsToArray } from 'client/models/Helper' import { + convertToMB, createSteps, - isBase64, encodeBase64, getUnknownAttributes, - convertToMB, + isBase64, } from 'client/utils' -import { T, MEMORY_RESIZE_OPTIONS } from 'client/constants' /** * Encodes the start script value to base64 if it is not already encoded. @@ -65,16 +65,26 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], { order: vmTemplate?.TEMPLATE?.INPUTS_ORDER, }) - const knownTemplate = schema.cast( - { - [GENERAL_ID]: { ...vmTemplate, ...vmTemplate?.TEMPLATE }, - [EXTRA_ID]: { ...vmTemplate?.TEMPLATE, USER_INPUTS: userInputs }, + const objectSchema = { + [GENERAL_ID]: { ...vmTemplate, ...vmTemplate?.TEMPLATE }, + [EXTRA_ID]: { + ...vmTemplate?.TEMPLATE, + USER_INPUTS: userInputs, }, - { - stripUnknown: true, - context: { ...vmTemplate, [EXTRA_ID]: vmTemplate.TEMPLATE }, + } + + // cast CPU_MODEL/FEATURES + if (vmTemplate?.TEMPLATE?.CPU_MODEL?.FEATURES) { + objectSchema[EXTRA_ID].CPU_MODEL = { + ...vmTemplate?.TEMPLATE?.CPU_MODEL, + FEATURES: (vmTemplate?.TEMPLATE?.CPU_MODEL?.FEATURES ?? '').split(','), } - ) + } + + const knownTemplate = schema.cast(objectSchema, { + stripUnknown: true, + context: { ...vmTemplate, [EXTRA_ID]: vmTemplate.TEMPLATE }, + }) const knownAttributes = { ...knownTemplate[GENERAL_ID], @@ -135,6 +145,12 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], { general.MEMORY = convertToMB(general.MEMORY, general.MEMORYUNIT) delete general.MEMORYUNIT + // cast CPU_MODEL/FEATURES + if (Array.isArray(extraTemplate?.CPU_MODEL?.FEATURES)) { + extraTemplate.CPU_MODEL.FEATURES = + extraTemplate.CPU_MODEL.FEATURES.join(', ') + } + return jsonToXml({ ...customVariables, ...extraTemplate, diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index a965334a52b..901cd0224b0 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -920,6 +920,7 @@ module.exports = { 'This attribute allows to define the type of firmware used to boot the VM', FirmwareSecure: 'Firmware secure', CpuModel: 'CPU Model', + CpuFeature: 'CPU Features', CustomPath: 'Customize with path', /* VM Template schema - OS & CPU - kernel */ Kernel: 'Kernel', diff --git a/src/fireedge/src/client/models/Host.js b/src/fireedge/src/client/models/Host.js index 1e0c2632ba4..76212715afe 100644 --- a/src/fireedge/src/client/models/Host.js +++ b/src/fireedge/src/client/models/Host.js @@ -130,6 +130,22 @@ export const getKvmCpuModels = (hosts = []) => { return [...new Set(hostData)] } +/** + * Returns list of KVM CPU Features available from the host pool. + * + * @param {Host[]} hosts - Hosts + * @returns {Array} List of KVM Machines from the pool + */ +export const getKvmCpuFeatures = (hosts = []) => { + const machineTypes = hosts + .filter((host) => host?.TEMPLATE?.HYPERVISOR === HYPERVISORS.kvm) + .map((host) => host.TEMPLATE?.KVM_CPU_FEATURES.split(',')) + .flat() + + // Removes the repeated + return [...new Set(machineTypes)] +} + /** * Returns list of KVM Machines available from the host pool. * diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js index ced5fb28e90..304a8af5121 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os.js @@ -295,7 +295,48 @@ define(function(require) { }); fillMachineTypesAndCPUModel(context); + fillCPUFeatures(context); + } + function fillCPUFeatures (context, cpuModel){ + OpenNebulaHost.list({ + data : {}, + timeout: true, + success: function (request, infoHosts){ + var cpuFeatures = [] + infoHosts.forEach((host)=> { + if(host && host.HOST && host.HOST.IM_MAD === 'kvm' && host.HOST.TEMPLATE && host.HOST.TEMPLATE.KVM_CPU_FEATURES){ + var arrayFeatures = host.HOST.TEMPLATE.KVM_CPU_FEATURES.split(",") + for (var i = 0; i < arrayFeatures.length; i++) { + var currentValue = arrayFeatures[i]; + if (cpuFeatures.indexOf(currentValue) === -1) { + cpuFeatures.push(currentValue); + } + } + } + }) + + var idSelector = 'feature-cpu' + var html = ''; + var inputFeatures = $('#cpu-features', context) + inputFeatures.find("#"+idSelector).remove() + $('#cpu-features', context).append(html); + if (cpuModel && cpuModel.FEATURES){ + var values = cpuModel.FEATURES.split(",") + $('#'+idSelector+' option').each(function(){ + var option = $(this); + var value = option.val(); + if ($.inArray(value, values) !== -1) { + option.prop("selected", true); + } + }) + } + } + }) } function fillMachineTypesAndCPUModel(context, cpuModel, machineType){ @@ -404,7 +445,6 @@ define(function(require) { var osJSON = templateJSON["OS"]; var modelJSON = templateJSON["CPU_MODEL"]; if (osJSON) { - if (osJSON["KERNEL_DS"] === undefined && osJSON["KERNEL"] !== undefined){ $("input[value=\"kernel_path\"]", context).click(); } @@ -445,7 +485,8 @@ define(function(require) { } fillMachineTypesAndCPUModel(context, modelJSON, osJSON); - + fillCPUFeatures(context, modelJSON) + delete templateJSON["OS"]; delete templateJSON["CPU_MODEL"]; } diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs index b81fb16d993..e9938b33c92 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/os/html.hbs @@ -306,11 +306,16 @@
-
+
+
+ +