-
- {sections.map(({ id, ...section }) => (
+ {sections.map(({ key, id, ...section }) => (
{
)
}
-const General = (initialValues) => {
- const isUpdate = initialValues?.NAME
- const initialHypervisor = initialValues?.TEMPLATE?.HYPERVISOR
+/**
+ * General configuration about VM Template.
+ *
+ * @param {VmTemplate} vmTemplate - VM Template
+ * @returns {object} General configuration step
+ */
+const General = (vmTemplate) => {
+ const isUpdate = vmTemplate?.NAME
+ const initialHypervisor = vmTemplate?.TEMPLATE?.HYPERVISOR
return {
id: STEP_ID,
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/informationSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/informationSchema.js
index 2da5a2cc2b..0e088bd604 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/informationSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/informationSchema.js
@@ -16,7 +16,7 @@
import { string } from 'yup'
import Image from 'client/components/Image'
-import { T, LOGO_IMAGES_URL, INPUT_TYPES, HYPERVISORS } from 'client/constants'
+import { T, STATIC_FILES_URL, INPUT_TYPES, HYPERVISORS } from 'client/constants'
import { Field, arrayToOptions } from 'client/utils'
/**
@@ -31,7 +31,7 @@ export const NAME = (isUpdate) => ({
.trim()
.required()
.default(() => undefined),
- grid: { sm: 6 },
+ grid: { md: 12 },
...(isUpdate && { fieldProps: { disabled: true } }),
})
@@ -40,10 +40,12 @@ export const DESCRIPTION = {
name: 'DESCRIPTION',
label: T.Description,
type: INPUT_TYPES.TEXT,
+ multiline: true,
validation: string()
.trim()
.notRequired()
.default(() => undefined),
+ grid: { md: 12 },
}
/** @type {Field} Hypervisor field */
@@ -68,40 +70,38 @@ export const LOGO = {
type: INPUT_TYPES.SELECT,
values: [
{ text: '-', value: '' },
- // client/assets/images/logos
- { text: 'Alpine Linux', value: 'alpine.png' },
- { text: 'ALT', value: 'alt.png' },
- { text: 'Arch', value: 'arch.png' },
- { text: 'CentOS', value: 'centos.png' },
- { text: 'Debian', value: 'debian.png' },
- { text: 'Devuan', value: 'devuan.png' },
- { text: 'Fedora', value: 'fedora.png' },
- { text: 'FreeBSD', value: 'freebsd.png' },
- { text: 'HardenedBSD', value: 'hardenedbsd.png' },
- { text: 'Knoppix', value: 'knoppix.png' },
- { text: 'Linux', value: 'linux.png' },
- { text: 'Oracle', value: 'oracle.png' },
- { text: 'RedHat', value: 'redhat.png' },
- { text: 'Suse', value: 'suse.png' },
- { text: 'Ubuntu', value: 'ubuntu.png' },
- { text: 'Windows xp', value: 'windowsxp.png' },
- { text: 'Windows 10', value: 'windows8.png' },
+ { text: 'Alpine Linux', value: 'images/logos/alpine.png' },
+ { text: 'ALT', value: 'images/logos/alt.png' },
+ { text: 'Arch', value: 'images/logos/arch.png' },
+ { text: 'CentOS', value: 'images/logos/centos.png' },
+ { text: 'Debian', value: 'images/logos/debian.png' },
+ { text: 'Devuan', value: 'images/logos/devuan.png' },
+ { text: 'Fedora', value: 'images/logos/fedora.png' },
+ { text: 'FreeBSD', value: 'images/logos/freebsd.png' },
+ { text: 'HardenedBSD', value: 'images/logos/hardenedbsd.png' },
+ { text: 'Knoppix', value: 'images/logos/knoppix.png' },
+ { text: 'Linux', value: 'images/logos/linux.png' },
+ { text: 'Oracle', value: 'images/logos/oracle.png' },
+ { text: 'RedHat', value: 'images/logos/redhat.png' },
+ { text: 'Suse', value: 'images/logos/suse.png' },
+ { text: 'Ubuntu', value: 'images/logos/ubuntu.png' },
+ { text: 'Windows xp', value: 'images/logos/windowsxp.png' },
+ { text: 'Windows 10', value: 'images/logos/windows8.png' },
],
renderValue: (value) => (
client/assets/images/logos/{value}.png
+ src={`${STATIC_FILES_URL}/${value}`}
/>
),
validation: string()
.trim()
.notRequired()
.default(() => undefined),
+ grid: { md: 12 },
}
/**
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/ownershipSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/ownershipSchema.js
index 8407173b91..e805a707ef 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/ownershipSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/ownershipSchema.js
@@ -29,6 +29,7 @@ export const UID_FIELD = {
const { data: users = [] } = useGetUsersQuery()
return arrayToOptions(users, {
+ addEmpty: false,
getText: ({ ID, NAME }) => `#${ID} ${NAME}`,
getValue: ({ ID }) => ID,
sorter: OPTION_SORTERS.numeric,
@@ -50,6 +51,7 @@ export const GID_FIELD = {
const { data: groups = [] } = useGetGroupsQuery()
return arrayToOptions(groups, {
+ addEmpty: false,
getText: ({ ID, NAME }) => `#${ID} ${NAME}`,
getValue: ({ ID }) => ID,
sorter: OPTION_SORTERS.numeric,
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/schema.js
index 604711831b..9b1ecf394d 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/schema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/schema.js
@@ -19,7 +19,7 @@ import {
FIELDS as INFORMATION_FIELDS,
HYPERVISOR_FIELD,
} from './informationSchema'
-import { FIELDS as CAPACITY_FIELDS } from './capacitySchema'
+import { MEMORY_FIELDS, CPU_FIELDS, VCPU_FIELDS } from './capacitySchema'
import { FIELDS as VM_GROUP_FIELDS } from './vmGroupSchema'
import { FIELDS as OWNERSHIP_FIELDS } from './ownershipSchema'
import { FIELDS as VCENTER_FIELDS } from './vcenterSchema'
@@ -41,12 +41,28 @@ const SECTIONS = (hypervisor, isUpdate) => [
id: 'information',
legend: T.Information,
required: true,
- fields: filterFieldsByHypervisor(INFORMATION_FIELDS(isUpdate), hypervisor),
+ fields: INFORMATION_FIELDS(isUpdate),
+ },
+ {
+ id: 'hypervisor',
+ legend: T.Hypervisor,
+ required: true,
+ fields: [HYPERVISOR_FIELD],
},
{
id: 'capacity',
- legend: T.Capacity,
- fields: filterFieldsByHypervisor(CAPACITY_FIELDS, hypervisor),
+ legend: T.Memory,
+ fields: filterFieldsByHypervisor(MEMORY_FIELDS, hypervisor),
+ },
+ {
+ id: 'capacity',
+ legend: T.PhysicalCpu,
+ fields: filterFieldsByHypervisor(CPU_FIELDS, hypervisor),
+ },
+ {
+ id: 'capacity',
+ legend: T.VirtualCpu,
+ fields: filterFieldsByHypervisor(VCPU_FIELDS, hypervisor),
},
{
id: 'ownership',
@@ -70,11 +86,10 @@ const SECTIONS = (hypervisor, isUpdate) => [
* @returns {BaseSchema} Step schema
*/
const SCHEMA = (hypervisor) =>
- getObjectSchemaFromFields([
- HYPERVISOR_FIELD,
- ...SECTIONS(hypervisor)
+ getObjectSchemaFromFields(
+ SECTIONS(hypervisor)
.map(({ fields }) => fields)
- .flat(),
- ])
+ .flat()
+ )
export { SECTIONS, SCHEMA }
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/styles.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/styles.js
index 2646764c51..7677c6520d 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/styles.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/styles.js
@@ -20,11 +20,11 @@ export default makeStyles((theme) => ({
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: theme.spacing(1),
- [theme.breakpoints.down('lg')]: {
+ [theme.breakpoints.down('md')]: {
gridTemplateColumns: '1fr',
},
},
- information: {
+ capacity: {
gridColumn: '1 / -1',
},
}))
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/vmGroupSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/vmGroupSchema.js
index 25c8d24135..6e27b81ea1 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/vmGroupSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/General/vmGroupSchema.js
@@ -28,6 +28,7 @@ export const VM_GROUP_FIELD = {
const { data: vmGroups = [] } = useGetVMGroupsQuery()
return arrayToOptions(vmGroups, {
+ addEmpty: false,
getText: ({ ID, NAME }) => `#${ID} ${NAME}`,
getValue: ({ ID }) => ID,
sorter: OPTION_SORTERS.numeric,
@@ -58,7 +59,7 @@ export const ROLE_FIELD = {
)
?.flat()
- return arrayToOptions(roles)
+ return arrayToOptions(roles, { addEmpty: false })
},
grid: { md: 12 },
validation: string()
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 07a43f903b..7819169304 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
@@ -23,14 +23,13 @@ import CustomVariables, {
STEP_ID as CUSTOM_ID,
} from 'client/components/Forms/VmTemplate/CreateForm/Steps/CustomVariables'
import { jsonToXml, userInputsToArray } from 'client/models/Helper'
-import { createSteps, isBase64 } from 'client/utils'
+import { createSteps, isBase64, encodeBase64 } from 'client/utils'
const Steps = createSteps([General, ExtraConfiguration, CustomVariables], {
transformInitialValue: (vmTemplate, schema) => {
- const userInputs = userInputsToArray(
- vmTemplate?.TEMPLATE?.USER_INPUTS,
- vmTemplate?.TEMPLATE?.INPUTS_ORDER
- )
+ const userInputs = userInputsToArray(vmTemplate?.TEMPLATE?.USER_INPUTS, {
+ order: vmTemplate?.TEMPLATE?.INPUTS_ORDER,
+ })
const knownTemplate = schema.cast(
{
@@ -56,7 +55,7 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], {
},
transformBeforeSubmit: (formData) => {
const {
- [GENERAL_ID]: general = {},
+ [GENERAL_ID]: { MODIFICATION: _, ...general } = {},
[CUSTOM_ID]: customVariables = {},
[EXTRA_ID]: {
CONTEXT: { START_SCRIPT, ENCODE_START_SCRIPT, ...restOfContext },
@@ -70,7 +69,7 @@ const Steps = createSteps([General, ExtraConfiguration, CustomVariables], {
// transform start script to base64 if needed
[ENCODE_START_SCRIPT ? 'START_SCRIPT_BASE64' : 'START_SCRIPT']:
ENCODE_START_SCRIPT && !isBase64(START_SCRIPT)
- ? btoa(unescape(encodeURIComponent(START_SCRIPT)))
+ ? encodeBase64(START_SCRIPT)
: START_SCRIPT,
}
const topology = ENABLE_NUMA ? { TOPOLOGY: restOfTopology } : {}
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js
index 5cc3af69ee..d6a17bb2bb 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js
@@ -13,54 +13,73 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-import { number } from 'yup'
+import { NumberSchema } from 'yup'
+import { getUserInputParams } from 'client/models/Helper'
+import {
+ Field,
+ schemaUserInput,
+ prettyBytes,
+ isDivisibleBy,
+} from 'client/utils'
+import { T, HYPERVISORS, VmTemplate } from 'client/constants'
-import { Field } from 'client/utils'
-import { T, INPUT_TYPES, HYPERVISORS } from 'client/constants'
-
-const commonValidation = number()
- .positive()
- .default(() => undefined)
-
-/** @type {Field} Memory field */
-const MEMORY = (hypervisor) => {
- let validation = commonValidation.required()
-
- if (hypervisor === HYPERVISORS.vcenter) {
- validation = validation.isDivisibleBy(4)
- }
-
- return {
- name: 'MEMORY',
- label: T.Memory,
- tooltip: T.MemoryConcept,
- type: INPUT_TYPES.TEXT,
- htmlType: 'number',
- validation,
- grid: { md: 12 },
- }
+const TRANSLATES = {
+ MEMORY: { name: 'MEMORY', label: T.Memory, tooltip: T.MemoryConcept },
+ CPU: { name: 'CPU', label: T.PhysicalCpu, tooltip: T.CpuConcept },
+ VCPU: { name: 'VCPU', label: T.VirtualCpu, tooltip: T.VirtualCpuConcept },
}
-/** @type {Field} Physical CPU field */
-const PHYSICAL_CPU = {
- name: 'CPU',
- label: T.PhysicalCpu,
- tooltip: T.PhysicalCpuConcept,
- type: INPUT_TYPES.TEXT,
- htmlType: 'number',
- validation: commonValidation.required(),
- grid: { md: 12 },
-}
+const valueLabelFormat = (value) => prettyBytes(value, 'MB')
-/** @type {Field} Virtual CPU field */
-const VIRTUAL_CPU = {
- name: 'VCPU',
- label: T.VirtualCpu,
- tooltip: T.VirtualCpuConcept,
- type: INPUT_TYPES.TEXT,
- htmlType: 'number',
- validation: commonValidation.notRequired(),
- grid: { md: 12 },
-}
+/**
+ * @param {VmTemplate} [vmTemplate] - VM Template
+ * @returns {Field[]} Basic configuration fields
+ */
+export const FIELDS = (vmTemplate) => {
+ const {
+ HYPERVISOR,
+ USER_INPUTS = {},
+ MEMORY = '',
+ CPU = '',
+ VCPU = '',
+ } = vmTemplate?.TEMPLATE || {}
-export const FIELDS = [MEMORY, PHYSICAL_CPU, VIRTUAL_CPU]
+ const {
+ MEMORY: memoryInput = `M|number|||${MEMORY}`,
+ CPU: cpuInput = `M|number-float|||${CPU}`,
+ VCPU: vcpuInput = `O|number|||${VCPU}`,
+ } = USER_INPUTS
+
+ return [
+ { name: 'MEMORY', ...getUserInputParams(memoryInput) },
+ { name: 'CPU', ...getUserInputParams(cpuInput) },
+ { name: 'VCPU', ...getUserInputParams(vcpuInput) },
+ ].map(({ name, options, ...userInput }) => {
+ const isMemory = name === 'MEMORY'
+ const isVCenter = HYPERVISOR === HYPERVISORS.vcenter
+ const divisibleBy4 = isVCenter && isMemory
+
+ const ensuredOptions = divisibleBy4
+ ? options?.filter((value) => isDivisibleBy(+value, 4))
+ : options
+
+ const schemaUi = schemaUserInput({ options: ensuredOptions, ...userInput })
+ const isNumber = schemaUi.validation instanceof NumberSchema
+
+ if (isNumber) {
+ // add positive number validator
+ isNumber && (schemaUi.validation &&= schemaUi.validation.positive())
+
+ // add label format on pretty bytes
+ isMemory &&
+ (schemaUi.fieldProps = { ...schemaUi.fieldProps, valueLabelFormat })
+
+ if (divisibleBy4) {
+ schemaUi.validation &&= schemaUi.validation.isDivisibleBy(4)
+ schemaUi.fieldProps = { ...schemaUi.fieldProps, step: 4 }
+ }
+ }
+
+ return { ...TRANSLATES[name], ...schemaUi, grid: { md: 12 } }
+ })
+}
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js
index 2b69d43f59..704bd33fb9 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js
@@ -22,23 +22,26 @@ import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/
import {
SCHEMA,
- FIELDS,
+ SECTIONS,
} from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema'
import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper'
import { T, RESOURCE_NAMES, VmTemplate } from 'client/constants'
export const STEP_ID = 'configuration'
-const Content = ({ hypervisor }) => {
+const Content = ({ vmTemplate }) => {
const classes = useStyles()
const { view, getResourceView } = useViews()
const sections = useMemo(() => {
+ const hypervisor = vmTemplate?.TEMPLATE?.HYPERVISOR
const resource = RESOURCE_NAMES.VM_TEMPLATE
const dialog = getResourceView(resource)?.dialogs?.instantiate_dialog
const sectionsAvailable = getSectionsAvailable(dialog, hypervisor)
- return FIELDS(hypervisor).filter(({ id }) => sectionsAvailable.includes(id))
+ return SECTIONS(vmTemplate).filter(({ id }) =>
+ sectionsAvailable.includes(id)
+ )
}, [view])
return (
@@ -57,26 +60,22 @@ const Content = ({ hypervisor }) => {
)
}
+Content.propTypes = {
+ vmTemplate: PropTypes.object,
+}
+
/**
* Basic configuration about VM Template.
*
* @param {VmTemplate} vmTemplate - VM Template
* @returns {object} Basic configuration step
*/
-const BasicConfiguration = (vmTemplate) => {
- const hypervisor = vmTemplate?.TEMPLATE?.HYPERVISOR
-
- return {
- id: STEP_ID,
- label: T.Configuration,
- resolver: () => SCHEMA(hypervisor),
- optionsValidate: { abortEarly: false },
- content: (props) => Content({ ...props, hypervisor }),
- }
-}
-
-Content.propTypes = {
- hypervisor: PropTypes.string,
-}
+const BasicConfiguration = (vmTemplate) => ({
+ id: STEP_ID,
+ label: T.Configuration,
+ resolver: () => SCHEMA(vmTemplate),
+ optionsValidate: { abortEarly: false },
+ content: (props) => Content({ ...props, vmTemplate }),
+})
export default BasicConfiguration
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js
index 5bce1cf3b7..dfa05fc644 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js
@@ -28,50 +28,59 @@ import {
filterFieldsByHypervisor,
getObjectSchemaFromFields,
Field,
+ Section,
} from 'client/utils'
-import { T, HYPERVISORS } from 'client/constants'
+import { T, VmTemplate } from 'client/constants'
/**
- * @param {HYPERVISORS} [hypervisor] - Template hypervisor
- * @returns {function(string):{ id: string, legend: string, fields: Field[] }[]} Fields
+ * @param {VmTemplate} [vmTemplate] - VM Template
+ * @returns {Section[]} Sections
*/
-const FIELDS = (hypervisor) => [
- {
- id: 'information',
- legend: T.Information,
- fields: filterFieldsByHypervisor(INFORMATION_FIELDS, hypervisor),
- },
- {
- id: 'capacity',
- legend: T.Capacity,
- fields: filterFieldsByHypervisor(CAPACITY_FIELDS, hypervisor),
- },
- {
- id: 'ownership',
- legend: T.Ownership,
- fields: filterFieldsByHypervisor(OWNERSHIP_FIELDS, hypervisor),
- },
- {
- id: 'vm_group',
- legend: T.VMGroup,
- fields: filterFieldsByHypervisor(VM_GROUP_FIELDS, hypervisor),
- },
- {
- id: 'vcenter',
- legend: T.vCenterDeployment,
- fields: filterFieldsByHypervisor([VCENTER_FOLDER_FIELD], hypervisor),
- },
-]
+const SECTIONS = (vmTemplate) => {
+ const hypervisor = vmTemplate?.TEMPLATE?.HYPERVISOR
+
+ return [
+ {
+ id: 'information',
+ legend: T.Information,
+ fields: filterFieldsByHypervisor(INFORMATION_FIELDS, hypervisor),
+ },
+ {
+ id: 'capacity',
+ legend: T.Capacity,
+ fields: filterFieldsByHypervisor(CAPACITY_FIELDS(vmTemplate), hypervisor),
+ },
+ {
+ id: 'ownership',
+ legend: T.Ownership,
+ fields: filterFieldsByHypervisor(OWNERSHIP_FIELDS, hypervisor),
+ },
+ {
+ id: 'vm_group',
+ legend: T.VMGroup,
+ fields: filterFieldsByHypervisor(VM_GROUP_FIELDS, hypervisor),
+ },
+ {
+ id: 'vcenter',
+ legend: T.vCenterDeployment,
+ fields: filterFieldsByHypervisor([VCENTER_FOLDER_FIELD], hypervisor),
+ },
+ ]
+}
/**
- * @param {HYPERVISORS} [hypervisor] - Template hypervisor
+ * @param {VmTemplate} [vmTemplate] - VM Template
+ * @returns {Field[]} Basic configuration fields
+ */
+const FIELDS = (vmTemplate) =>
+ SECTIONS(vmTemplate)
+ .map(({ fields }) => fields)
+ .flat()
+
+/**
+ * @param {VmTemplate} [vmTemplate] - VM Template
* @returns {BaseSchema} Step schema
*/
-const SCHEMA = (hypervisor) =>
- getObjectSchemaFromFields(
- FIELDS(hypervisor)
- .map(({ fields }) => fields)
- .flat()
- )
+const SCHEMA = (vmTemplate) => getObjectSchemaFromFields(FIELDS(vmTemplate))
-export { FIELDS, SCHEMA }
+export { SECTIONS, FIELDS, SCHEMA }
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs/index.js
index 34fd785ca1..1c2aca48ed 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs/index.js
@@ -20,8 +20,7 @@ import {
FIELDS,
SCHEMA,
} from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs/schema'
-import { userInputsToArray } from 'client/models/Helper'
-import { T, VmTemplate } from 'client/constants'
+import { T, UserInputObject } from 'client/constants'
import { FormWithSchema } from 'client/components/Forms'
export const STEP_ID = 'user_inputs'
@@ -42,22 +41,15 @@ Content.propTypes = {
/**
* User inputs step.
*
- * @param {VmTemplate} vmTemplate - VM Template
+ * @param {UserInputObject[]} userInputs - User inputs
* @returns {object} User inputs step
*/
-const UserInputsStep = (vmTemplate) => {
- const userInputs = userInputsToArray(
- vmTemplate?.TEMPLATE?.USER_INPUTS,
- vmTemplate?.TEMPLATE?.INPUTS_ORDER
- )
-
- return {
- id: STEP_ID,
- label: T.UserInputs,
- optionsValidate: { abortEarly: false },
- resolver: SCHEMA(userInputs),
- content: (props) => Content({ ...props, userInputs }),
- }
-}
+const UserInputsStep = (userInputs) => ({
+ id: STEP_ID,
+ label: T.UserInputs,
+ optionsValidate: { abortEarly: false },
+ resolver: SCHEMA(userInputs),
+ content: (props) => Content({ ...props, userInputs }),
+})
export default UserInputsStep
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js
index 24e77aa5a8..b3ff7b6864 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js
@@ -22,16 +22,21 @@ import UserInputs, {
import ExtraConfiguration, {
STEP_ID as EXTRA_ID,
} from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
-import { jsonToXml } from 'client/models/Helper'
+import { jsonToXml, userInputsToArray } from 'client/models/Helper'
import { createSteps } from 'client/utils'
const Steps = createSteps(
- (vmTemplate) =>
- [
+ (vmTemplate) => {
+ const userInputs = userInputsToArray(vmTemplate?.TEMPLATE?.USER_INPUTS, {
+ order: vmTemplate?.TEMPLATE?.INPUTS_ORDER,
+ })
+
+ return [
BasicConfiguration,
- vmTemplate?.TEMPLATE?.USER_INPUTS && UserInputs,
+ !!userInputs.length && (() => UserInputs(userInputs)),
ExtraConfiguration,
- ].filter(Boolean),
+ ].filter(Boolean)
+ },
{
transformInitialValue: (vmTemplate, schema) => {
const initialValue = schema.cast(
diff --git a/src/fireedge/src/client/components/HOC/AsyncLoadForm.js b/src/fireedge/src/client/components/HOC/AsyncLoadForm.js
index b121a77660..4cb38d9b5c 100644
--- a/src/fireedge/src/client/components/HOC/AsyncLoadForm.js
+++ b/src/fireedge/src/client/components/HOC/AsyncLoadForm.js
@@ -124,4 +124,4 @@ AsyncLoadForm.propTypes = {
componentToLoad: PropTypes.string,
}
-export default AsyncLoadForm
+export { AsyncLoadForm }
diff --git a/src/fireedge/src/client/components/HOC/index.js b/src/fireedge/src/client/components/HOC/index.js
index 43c910d38d..5b9446f4ae 100644
--- a/src/fireedge/src/client/components/HOC/index.js
+++ b/src/fireedge/src/client/components/HOC/index.js
@@ -16,9 +16,6 @@
export { default as AuthLayout } from 'client/components/HOC/AuthLayout'
export { default as ConditionalWrap } from 'client/components/HOC/ConditionalWrap'
export { default as InternalLayout } from 'client/components/HOC/InternalLayout'
-export {
- default as AsyncLoadForm,
- ConfigurationProps,
-} from 'client/components/HOC/AsyncLoadForm'
+export * from 'client/components/HOC/AsyncLoadForm'
export * from 'client/components/HOC/Translate'
diff --git a/src/fireedge/src/client/components/Tabs/VmTemplate/Info/index.js b/src/fireedge/src/client/components/Tabs/VmTemplate/Info/index.js
index 850488a2e7..cc0e2f5bbb 100644
--- a/src/fireedge/src/client/components/Tabs/VmTemplate/Info/index.js
+++ b/src/fireedge/src/client/components/Tabs/VmTemplate/Info/index.js
@@ -41,17 +41,17 @@ const VmTemplateInfoTab = ({ tabProps = {}, id }) => {
ownership_panel: ownershipPanel,
} = tabProps
- const [changeOwnership] = useChangeTemplatePermissionsMutation()
- const [changePermissions] = useChangeTemplateOwnershipMutation()
+ const [changePermissions] = useChangeTemplatePermissionsMutation()
+ const [changeOwnership] = useChangeTemplateOwnershipMutation()
const { data: template = {} } = useGetTemplateQuery({ id })
- const { ID, UNAME, UID, GNAME, GID, PERMISSIONS } = template
+ const { UNAME, UID, GNAME, GID, PERMISSIONS } = template
const handleChangeOwnership = async (newOwnership) => {
- await changeOwnership({ id: ID, ...newOwnership })
+ await changeOwnership({ id, ...newOwnership })
}
const handleChangePermission = async (newPermission) => {
- await changePermissions({ id: ID, ...newPermission })
+ await changePermissions({ id, ...newPermission })
}
const getActions = (actions) => Helper.getActionsAvailable(actions)
@@ -72,6 +72,7 @@ const VmTemplateInfoTab = ({ tabProps = {}, id }) => {
{permissionsPanel?.enabled && (
{
otherUse={PERMISSIONS.OTHER_U}
otherManage={PERMISSIONS.OTHER_M}
otherAdmin={PERMISSIONS.OTHER_A}
- handleEdit={handleChangePermission}
/>
)}
{ownershipPanel?.enabled && (
)}
diff --git a/src/fireedge/src/client/components/Tabs/VmTemplate/Info/information.js b/src/fireedge/src/client/components/Tabs/VmTemplate/Info/information.js
index 1555cebea0..e40ff977ee 100644
--- a/src/fireedge/src/client/components/Tabs/VmTemplate/Info/information.js
+++ b/src/fireedge/src/client/components/Tabs/VmTemplate/Info/information.js
@@ -24,7 +24,7 @@ import { timeToString, levelLockToString } from 'client/models/Helper'
import {
T,
VM_TEMPLATE_ACTIONS,
- LOGO_IMAGES_URL,
+ STATIC_FILES_URL,
VmTemplate,
} from 'client/constants'
@@ -67,7 +67,7 @@ const InformationPanel = ({ template = {}, actions }) => {
},
LOGO && {
name: T.Logo,
- value: ,
+ value: ,
dataCy: 'logo',
},
].filter(Boolean)
diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js
index 9158983f42..02670010b8 100644
--- a/src/fireedge/src/client/constants/translates.js
+++ b/src/fireedge/src/client/constants/translates.js
@@ -484,6 +484,9 @@ module.exports = {
DefaultNicFilter: 'Default network filtering rule for all NICs',
/* VM Template schema - capacity */
MaxMemory: 'Max memory',
+ MaxMemoryConcept: `
+ This value sets the maximum value of the MEMORY allowed to be modified
+ after instantiation, through the Capacity Resize options of instantiated VMs`,
MemoryModification: 'Memory modification',
AllowUsersToModifyMemory:
"Allow users to modify this template's default memory on instantiate",
@@ -492,11 +495,15 @@ module.exports = {
Percentage of CPU divided by 100 required for the
Virtual Machine. Half a processor is written 0.5`,
MaxVirtualCpu: 'Max Virtual CPU',
+ MaxVirtualCpuConcept: `
+ This value sets the maximum value of the VCPU allowed to be modified
+ after instantiation, through the Capacity Resize options of instantiated VMs`,
+ CpuModification: 'CPU modification',
+ AllowUsersToModifyCpu:
+ "Allow users to modify this template's default CPU on instantiate",
VirtualCpuConcept: `
Number of virtual cpus. This value is optional, the default
hypervisor behavior is used, usually one virtual CPU`,
- AllowUsersToModifyCpu:
- "Allow users to modify this template's default CPU on instantiate",
VirtualCpuModification: 'Virtual CPU modification',
AllowUsersToModifyVirtualCpu:
"Allow users to modify this template's default Virtual CPU on instantiate",
diff --git a/src/fireedge/src/client/constants/userInput.js b/src/fireedge/src/client/constants/userInput.js
index a2a8d906a8..5f97ef99b9 100644
--- a/src/fireedge/src/client/constants/userInput.js
+++ b/src/fireedge/src/client/constants/userInput.js
@@ -16,6 +16,7 @@
/**
* @typedef {(
+ * 'fixed' |
* 'text' |
* 'text64' |
* 'password' |
@@ -64,6 +65,7 @@
/** @enum {UserInputType} User input types */
export const USER_INPUT_TYPES = {
+ fixed: 'fixed',
text: 'text',
text64: 'text64',
password: 'password',
diff --git a/src/fireedge/src/client/features/OneApi/vmTemplate.js b/src/fireedge/src/client/features/OneApi/vmTemplate.js
index 84e0d11139..fea81b4a18 100644
--- a/src/fireedge/src/client/features/OneApi/vmTemplate.js
+++ b/src/fireedge/src/client/features/OneApi/vmTemplate.js
@@ -215,6 +215,34 @@ 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]
+
+ draft.PERMISSIONS[ensuredName] = value
+ })
+ })
+ )
+
+ queryFulfilled.catch(patchResult.undo)
+ },
}),
changeTemplateOwnership: builder.mutation({
/**
@@ -235,6 +263,19 @@ 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)
+ })
+ )
+
+ queryFulfilled.catch(patchResult.undo)
+ },
}),
renameTemplate: builder.mutation({
/**
diff --git a/src/fireedge/src/client/models/Helper.js b/src/fireedge/src/client/models/Helper.js
index 4503da7c98..691911df2b 100644
--- a/src/fireedge/src/client/models/Helper.js
+++ b/src/fireedge/src/client/models/Helper.js
@@ -339,7 +339,7 @@ export const getUserInputString = (userInput) => {
// mandatory|type|description|range/options/' '|defaultValue
const uiString = [mandatoryString, type, description]
- range?.length > 0
+ ;[USER_INPUT_TYPES.range, USER_INPUT_TYPES.rangeFloat].includes(type)
? uiString.push(range)
: options?.length > 0
? uiString.push(options.join(LIST_SEPARATOR))
@@ -352,16 +352,19 @@ export const getUserInputString = (userInput) => {
* Get list of user inputs defined in OpenNebula template.
*
* @param {object} userInputs - List of user inputs in string format
- * @param {string} [inputsOrder] - List separated by comma of input names
+ * @param {object} [options] - Options to filter user inputs
+ * @param {boolean} [options.filterCapacityInputs]
+ * - If false, will not filter capacity inputs: MEMORY, CPU, VCPU. By default `true`
+ * @param {string} [options.order] - List separated by comma of input names
* @example
* const userInputs = {
* "INPUT-1": "O|text|Description1| |text1",
* "INPUT-2": "M|text|Description2| |text2"
* }
*
- * const inputsOrder = "INPUT-2,INPUT-1"
+ * const order = "INPUT-2,INPUT-1"
*
- * => userInputsToArray(userInputs, inputsOrder) => [{
+ * => userInputsToArray(userInputs, { order }) => [{
* name: 'INPUT-1',
* mandatory: false,
* type: 'text',
@@ -377,17 +380,33 @@ export const getUserInputString = (userInput) => {
* }]
* @returns {UserInputObject[]} User input object
*/
-export const userInputsToArray = (userInputs = {}, inputsOrder) => {
- const orderedList = inputsOrder?.split(',') ?? []
+export const userInputsToArray = (
+ userInputs = {},
+ { filterCapacityInputs = true, order } = {}
+) => {
+ const orderedList = order?.split(',') ?? []
+ const userInputsArray = Object.entries(userInputs)
- return Object.entries(userInputs)
- .map(([name, ui]) => ({ name, ...getUserInputParams(ui) }))
- .sort((a, b) => {
+ let list = userInputsArray.map(([name, ui]) => ({
+ name: `${name}`.toUpperCase(),
+ ...(typeof ui === 'string' ? getUserInputParams(ui) : ui),
+ }))
+
+ if (filterCapacityInputs) {
+ const capacityInputs = ['MEMORY', 'CPU', 'VCPU']
+ list = list.filter((ui) => !capacityInputs.includes(ui.name))
+ }
+
+ if (orderedList.length) {
+ list = list.sort((a, b) => {
const upperAName = a.name?.toUpperCase?.()
const upperBName = b.name?.toUpperCase?.()
return orderedList.indexOf(upperAName) - orderedList.indexOf(upperBName)
})
+ }
+
+ return list
}
/**
diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js
index f6cee39718..16c28f01ed 100644
--- a/src/fireedge/src/client/utils/helpers.js
+++ b/src/fireedge/src/client/utils/helpers.js
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
+import { v4 as uuidv4 } from 'uuid'
import DOMPurify from 'dompurify'
import { object, reach, ObjectSchema, BaseSchema } from 'yup'
import { isMergeableObject } from 'client/utils/merge'
@@ -41,8 +42,7 @@ export const isExternalURL = (url) => /^(http|https):/g.test(url)
*
* @returns {string} Random key
*/
-export const generateKey = () =>
- String(new Date().getTime() + Math.random()).replace('.', '')
+export const generateKey = () => uuidv4()
/**
* Sanitizes HTML and prevents XSS attacks.
@@ -443,15 +443,6 @@ export const isBase64 = (stringToValidate, options = {}) => {
return regex.test(stringToValidate)
}
-/**
- * Check if value is divisible by 4.
- *
- * @param {string|number} value - Number to check
- * @returns {boolean} Returns `true` if string is divisible by 4
- */
-export const isDivisibleBy4 = (value) =>
- /[048]|\d*([02468][048]|[13579][26])/g.test(value)
-
/**
* Check if value is divisible by another number.
*
diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js
index ef08e9532e..7a9ee6c233 100644
--- a/src/fireedge/src/client/utils/schema.js
+++ b/src/fireedge/src/client/utils/schema.js
@@ -115,6 +115,8 @@ import { stringToBoolean } from 'client/models/Helper'
* - Filters the field when the driver is not include on list
* @property {TextFieldProps|CheckboxProps|InputBaseComponentProps} [fieldProps]
* - Extra properties to material-ui field
+ * @property {boolean|DependOfCallback} [readOnly]
+ * - If `true`, the field is read only
* @property {function(string|number):any} [renderValue]
* - Render the current selected value inside selector input
* - **Only for select inputs.**
@@ -218,12 +220,8 @@ const getRange = (options) => options?.split?.('..').map(parseFloat)
const getValuesFromArray = (options, separator = SEMICOLON_CHAR) =>
options?.split(separator)
-const getOptionsFromList = (options) =>
- options
- ?.map((option) =>
- typeof option === 'string' ? { text: option, value: option } : option
- )
- ?.filter(({ text, value } = {}) => text && value)
+const getOptionsFromList = (options = []) =>
+ arrayToOptions([...new Set(options)])
const parseUserInputValue = (value) => {
if (value === true) {
@@ -257,6 +255,22 @@ export const schemaUserInput = ({
default: defaultValue,
}) => {
switch (type) {
+ case USER_INPUT_TYPES.fixed: {
+ const isNumeric = !isNaN(defaultValue)
+ const ensuredValue = isNumeric ? parseFloat(defaultValue) : defaultValue
+ const validation = isNumeric ? number() : string().trim()
+
+ return {
+ type: INPUT_TYPES.TEXT,
+ htmlType: isNaN(+defaultValue) ? 'text' : 'number',
+ validation: validation
+ .default(ensuredValue)
+ // ensures to send the value
+ .afterSubmit(() => defaultValue),
+ fieldProps: { disabled: true },
+ readOnly: true,
+ }
+ }
case USER_INPUT_TYPES.text:
case USER_INPUT_TYPES.text64:
case USER_INPUT_TYPES.password:
@@ -311,7 +325,8 @@ export const schemaUserInput = ({
}
case USER_INPUT_TYPES.list: {
const values = getOptionsFromList(options)
- const firstOption = values?.[0]?.value ?? undefined
+ const optionValues = values.map(({ value }) => value).filter(Boolean)
+ const firstOption = optionValues[0] ?? undefined
return {
values,
@@ -319,7 +334,7 @@ export const schemaUserInput = ({
validation: string()
.trim()
.concat(requiredSchema(mandatory, string()))
- .oneOf(values.map(({ value }) => value))
+ .oneOf(optionValues)
.default(() => defaultValue || firstOption),
}
}