diff --git a/src/fireedge/src/client/assets/images/logos/alpine.png b/src/fireedge/src/client/assets/images/logos/alpine.png
new file mode 100644
index 0000000000..66592c3660
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/alpine.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/alt.png b/src/fireedge/src/client/assets/images/logos/alt.png
new file mode 100644
index 0000000000..647fe2f1c4
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/alt.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/arch.png b/src/fireedge/src/client/assets/images/logos/arch.png
new file mode 100644
index 0000000000..11a3aa6430
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/arch.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/centos.png b/src/fireedge/src/client/assets/images/logos/centos.png
new file mode 100644
index 0000000000..23e1696a11
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/centos.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/debian.png b/src/fireedge/src/client/assets/images/logos/debian.png
new file mode 100644
index 0000000000..59d85ce0b7
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/debian.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/devuan.png b/src/fireedge/src/client/assets/images/logos/devuan.png
new file mode 100644
index 0000000000..8df52556fd
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/devuan.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/fedora.png b/src/fireedge/src/client/assets/images/logos/fedora.png
new file mode 100644
index 0000000000..476e2255d8
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/fedora.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/freebsd.png b/src/fireedge/src/client/assets/images/logos/freebsd.png
new file mode 100644
index 0000000000..9767cecbf3
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/freebsd.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/hardenedbsd.png b/src/fireedge/src/client/assets/images/logos/hardenedbsd.png
new file mode 100644
index 0000000000..23acdf82cd
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/hardenedbsd.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/knoppix.png b/src/fireedge/src/client/assets/images/logos/knoppix.png
new file mode 100644
index 0000000000..ae62016ada
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/knoppix.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/linux.png b/src/fireedge/src/client/assets/images/logos/linux.png
new file mode 100644
index 0000000000..45e219a929
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/linux.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/oracle.png b/src/fireedge/src/client/assets/images/logos/oracle.png
new file mode 100644
index 0000000000..7b64b4d015
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/oracle.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/redhat.png b/src/fireedge/src/client/assets/images/logos/redhat.png
new file mode 100644
index 0000000000..63d0bae718
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/redhat.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/suse.png b/src/fireedge/src/client/assets/images/logos/suse.png
new file mode 100644
index 0000000000..8130fbe097
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/suse.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/ubuntu.png b/src/fireedge/src/client/assets/images/logos/ubuntu.png
new file mode 100644
index 0000000000..a00dd5edb2
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/ubuntu.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/windows8.png b/src/fireedge/src/client/assets/images/logos/windows8.png
new file mode 100644
index 0000000000..7acdd9889c
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/windows8.png differ
diff --git a/src/fireedge/src/client/assets/images/logos/windowsxp.png b/src/fireedge/src/client/assets/images/logos/windowsxp.png
new file mode 100644
index 0000000000..8503f76a00
Binary files /dev/null and b/src/fireedge/src/client/assets/images/logos/windowsxp.png differ
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/CommonFields.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/CommonFields.js
index d8b3d3666e..268e2ea128 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/CommonFields.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/CommonFields.js
@@ -39,7 +39,6 @@ export const READONLY = {
notOnHypervisors: [vcenter],
type: INPUT_TYPES.SELECT,
values: [
- { text: '', value: '' },
{ text: T.Yes, value: 'YES' },
{ text: T.No, value: 'NO' }
],
@@ -47,7 +46,7 @@ export const READONLY = {
.string()
.trim()
.notRequired()
- .default(undefined)
+ .default(() => 'NO')
}
export const DEV_PREFIX = {
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js
index 6031663888..43b756ac61 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js
@@ -14,33 +14,34 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useCallback } from 'react'
+import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
-
-import {
- SCHEMA,
- FIELDS
-} from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/schema'
+import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/schema'
import { T } from 'client/constants'
export const STEP_ID = 'advanced'
+const Content = ({ hypervisor }) => (
+
+)
+
const AdvancedOptions = ({ hypervisor } = {}) => ({
id: STEP_ID,
label: T.AdvancedOptions,
resolver: () => SCHEMA(hypervisor),
optionsValidate: { abortEarly: false },
- content: useCallback(
- () => (
-
- ),
- [hypervisor]
- )
+ content: () => Content({ hypervisor })
})
+Content.propTypes = {
+ hypervisor: PropTypes.any,
+ data: PropTypes.any,
+ setFormData: PropTypes.func
+}
+
export default AdvancedOptions
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js
index a78626e2bb..c6d8976352 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js
@@ -14,55 +14,50 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useCallback } from 'react'
+import PropTypes from 'prop-types'
import { useListForm } from 'client/hooks'
import { ImagesTable } from 'client/components/Tables'
-
-import {
- SCHEMA
-} from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema'
+import { SCHEMA } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema'
import { T } from 'client/constants'
export const STEP_ID = 'image'
+const Content = ({ data, setFormData }) => {
+ const { ID } = data?.[0] ?? {}
+
+ const {
+ handleSelect,
+ handleClear
+ } = useListForm({ key: STEP_ID, setList: setFormData })
+
+ const handleSelectedRows = rows => {
+ const { original = {} } = rows?.[0] ?? {}
+
+ original.ID !== undefined ? handleSelect(original) : handleClear()
+ }
+
+ return (
+
+ )
+}
+
const ImageStep = () => ({
id: STEP_ID,
label: T.Image,
- resolver: () => SCHEMA,
- content: useCallback(
- ({ data, setFormData }) => {
- const selectedImage = data?.[0]
-
- const {
- handleSelect,
- handleClear
- } = useListForm({ key: STEP_ID, setList: setFormData })
-
- const handleSelectedRows = rows => {
- const { original } = rows?.[0] ?? {}
- const { ID, NAME, UID, UNAME } = original ?? {}
-
- const image = {
- IMAGE_ID: ID,
- IMAGE: NAME,
- IMAGE_UID: UID,
- IMAGE_UNAME: UNAME
- }
-
- ID !== undefined ? handleSelect(image) : handleClear()
- }
-
- return (
-
- )
- }, [])
+ resolver: SCHEMA,
+ content: Content
})
+Content.propTypes = {
+ data: PropTypes.any,
+ setFormData: PropTypes.func
+}
+
export default ImageStep
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js
index 866b07ca12..f1024a0e2e 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js
@@ -20,4 +20,5 @@ export const SCHEMA = yup
.min(1, 'Select image')
.max(1, 'Max. one image selected')
.required('Image field is required')
- .default([])
+ .ensure()
+ .default(() => [])
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js
index ad9a6ae2cd..10767ffafb 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js
@@ -13,10 +13,51 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-import ImagesTable from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable'
-import AdvancedOptions from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions'
-import { createSteps } from 'client/utils'
+import ImagesTable, { STEP_ID as STEP_IMAGE } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable'
+import AdvancedOptions, { STEP_ID as STEP_ADVANCED } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions'
+import { mapUserInputs, createSteps } from 'client/utils'
-const Steps = createSteps([ImagesTable, AdvancedOptions])
+const Steps = createSteps(
+ [ImagesTable, AdvancedOptions],
+ {
+ transformInitialValue: initialValue => {
+ const {
+ IMAGE,
+ IMAGE_ID,
+ IMAGE_UID,
+ IMAGE_UNAME,
+ IMAGE_STATE,
+ ...diskProps
+ } = initialValue ?? {}
+
+ return {
+ [STEP_IMAGE]: [{
+ ...diskProps,
+ NAME: IMAGE,
+ ID: IMAGE_ID,
+ UID: IMAGE_UID,
+ UNAME: IMAGE_UNAME,
+ STATE: IMAGE_STATE
+ }],
+ [STEP_ADVANCED]: initialValue
+ }
+ },
+ transformBeforeSubmit: formData => {
+ const { [STEP_IMAGE]: [image] = [], [STEP_ADVANCED]: advanced } = formData
+ const { ID, NAME, UID, UNAME, STATE, SIZE, ...imageProps } = image ?? {}
+
+ return {
+ ...imageProps,
+ IMAGE: NAME,
+ IMAGE_ID: ID,
+ IMAGE_UID: UID,
+ IMAGE_UNAME: UNAME,
+ IMAGE_STATE: STATE,
+ ORIGINAL_SIZE: SIZE,
+ ...mapUserInputs(advanced)
+ }
+ }
+ }
+)
export default Steps
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js
index bbf54c4813..db344cd5ba 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js
@@ -14,31 +14,34 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useCallback } from 'react'
+import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
-
-import {
- SCHEMA,
- FIELDS
-} from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/schema'
+import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/schema'
import { T } from 'client/constants'
export const STEP_ID = 'advanced'
+const Content = ({ hypervisor }) => (
+
+)
+
const AdvancedOptions = ({ hypervisor } = {}) => ({
id: STEP_ID,
label: T.AdvancedOptions,
resolver: () => SCHEMA(hypervisor),
optionsValidate: { abortEarly: false },
- content: useCallback(
- () => ,
- [hypervisor]
- )
+ content: () => Content({ hypervisor })
})
+Content.propTypes = {
+ hypervisor: PropTypes.any,
+ data: PropTypes.any,
+ setFormData: PropTypes.func
+}
+
export default AdvancedOptions
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js
index c62bc31cc5..5069290aa4 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js
@@ -14,31 +14,34 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useCallback } from 'react'
+import PropTypes from 'prop-types'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
-
-import {
- SCHEMA,
- FIELDS
-} from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/schema'
+import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/schema'
import { T } from 'client/constants'
export const STEP_ID = 'configuration'
+const Content = ({ hypervisor }) => (
+
+)
+
const BasicConfiguration = ({ hypervisor } = {}) => ({
id: STEP_ID,
label: T.Configuration,
resolver: () => SCHEMA(hypervisor),
optionsValidate: { abortEarly: false },
- content: useCallback(
- () => ,
- [hypervisor]
- )
+ content: () => Content({ hypervisor })
})
+Content.propTypes = {
+ hypervisor: PropTypes.any,
+ data: PropTypes.any,
+ setFormData: PropTypes.func
+}
+
export default BasicConfiguration
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js
index c8df7059ba..29fdbb810e 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js
@@ -13,10 +13,28 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-import BasicConfiguration from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration'
-import AdvancedOptions from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions'
-import { createSteps } from 'client/utils'
+import BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration'
+import AdvancedOptions, { STEP_ID as ADVANCED_ID } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions'
+import { mapUserInputs, createSteps } from 'client/utils'
-const Steps = createSteps([BasicConfiguration, AdvancedOptions])
+const Steps = createSteps(
+ [BasicConfiguration, AdvancedOptions],
+ {
+ transformInitialValue: (disk = {}, schema) => ({
+ ...schema.cast({
+ [BASIC_ID]: disk,
+ [ADVANCED_ID]: disk
+ }, { stripUnknown: true })
+ }),
+ transformBeforeSubmit: formData => {
+ const {
+ [BASIC_ID]: configuration = {},
+ [ADVANCED_ID]: advanced = {}
+ } = formData ?? {}
+
+ return { ...mapUserInputs(advanced), ...mapUserInputs(configuration) }
+ }
+ }
+)
export default Steps
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js
index 0a8c73a6c4..d1e3006421 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js
@@ -20,4 +20,5 @@ export const SCHEMA = yup
.min(1, 'Select network')
.max(1, 'Max. one network selected')
.required('Network field is required')
- .default([])
+ .ensure()
+ .default(() => [])
diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js
index 3a3e6f915c..4622c823bf 100644
--- a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js
+++ b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js
@@ -13,16 +13,38 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-import NetworksTable, { STEP_ID as NETWORK_STEP } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable'
-import AdvancedOptions, { STEP_ID as ADVANCED_STEP } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions'
+import NetworksTable, { STEP_ID as NETWORK_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable'
+import AdvancedOptions, { STEP_ID as ADVANCED_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions'
import { mapUserInputs, createSteps } from 'client/utils'
const Steps = createSteps(
[NetworksTable, AdvancedOptions],
{
+ transformInitialValue: nic => {
+ const {
+ NETWORK,
+ NETWORK_ID: ID,
+ NETWORK_UID,
+ NETWORK_UNAME,
+ SECURITY_GROUPS,
+ ...rest
+ } = nic ?? {}
+
+ return {
+ [NETWORK_ID]: [{
+ ...nic,
+ ID,
+ NAME: NETWORK,
+ UID: NETWORK_UID,
+ UNAME: NETWORK_UNAME,
+ SECURITY_GROUPS
+ }],
+ [ADVANCED_ID]: rest
+ }
+ },
transformBeforeSubmit: formData => {
- const { [NETWORK_STEP]: network, [ADVANCED_STEP]: advanced } = formData
- const { ID, NAME, UID, UNAME, SECURITY_GROUPS } = network?.[0]
+ const { [NETWORK_ID]: [network] = [], [ADVANCED_ID]: advanced } = formData
+ const { ID, NAME, UID, UNAME, SECURITY_GROUPS } = network ?? {}
return {
NETWORK_ID: ID,
@@ -32,19 +54,6 @@ const Steps = createSteps(
SECURITY_GROUPS,
...mapUserInputs(advanced)
}
- },
- transformInitialValue: initialValue => {
- const { NETWORK_ID, NETWORK, NETWORK_UID, NETWORK_UNAME, ...rest } = initialValue ?? {}
-
- return {
- [NETWORK_STEP]: [{
- ID: NETWORK_ID,
- NAME: NETWORK,
- UID: NETWORK_UID,
- UNAME: NETWORK_UNAME
- }],
- [ADVANCED_STEP]: rest
- }
}
}
)
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js
new file mode 100644
index 0000000000..87ed298d18
--- /dev/null
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js
@@ -0,0 +1,21 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, 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 { createForm } from 'client/utils'
+import { SCHEMA, FIELDS } from 'client/components/Forms/VmTemplate/CloneForm/schema'
+
+const CloneForm = createForm(SCHEMA, FIELDS)
+
+export default CloneForm
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js
new file mode 100644
index 0000000000..3ecf1b3409
--- /dev/null
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js
@@ -0,0 +1,59 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, 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. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { string, boolean, object } from 'yup'
+import { INPUT_TYPES } from 'client/constants'
+import { getValidationFromFields } from 'client/utils'
+
+const PREFIX = {
+ name: 'prefix',
+ label: 'Prefix',
+ tooltip: `
+ Several templates are selected,
+ please choose prefix to name the new copies.`,
+ type: INPUT_TYPES.TEXT,
+ validation: string()
+ .trim()
+ .required('Prefix field is required')
+ .default(() => 'Copy of ')
+}
+
+const NAME = {
+ name: 'name',
+ label: 'Name',
+ tooltip: 'Name for the new template.',
+ type: INPUT_TYPES.TEXT,
+ validation: string()
+ .trim()
+ .required('Name field is required')
+ .default(() => '')
+}
+
+const IMAGES = {
+ name: 'image',
+ label: 'Clone with images',
+ tooltip: `
+ You can also clone any Image referenced inside this Template.
+ They will be cloned to a new Image, and made persistent.`,
+ type: INPUT_TYPES.CHECKBOX,
+ validation: boolean().default(() => false),
+ grid: { md: 12 }
+}
+
+export const FIELDS = ({ isMultiple } = {}) =>
+ [isMultiple ? PREFIX : NAME, IMAGES]
+
+export const SCHEMA = props => object(getValidationFromFields(FIELDS(props)))
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 6b10bd9a54..bb18176c70 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
@@ -14,13 +14,9 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useMemo } from 'react'
-import { useFormContext } from 'react-hook-form'
-
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/styles'
-import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable'
import { SCHEMA, FIELDS } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
@@ -29,8 +25,6 @@ export const STEP_ID = 'configuration'
const Content = () => {
const classes = useStyles()
- const { watch } = useFormContext()
- const selectedVmTemplate = useMemo(() => watch(`${TEMPLATE_ID}[0]`), [])
return (
@@ -47,12 +41,6 @@ const Content = () => {
legend={Tr(T.Capacity)}
id={STEP_ID}
/>
-
DISK_FIELDS(vmTemplate),
+ // DISK: vmTemplate => DISK_FIELDS(vmTemplate),
OWNERSHIP: OWNERSHIP_FIELDS,
VM_GROUP: VM_GROUP_FIELDS,
VCENTER: VCENTER_FIELDS
@@ -35,7 +34,7 @@ export const FIELDS = {
export const SCHEMA = object()
.concat(INFORMATION_SCHEMA)
.concat(CAPACITY_SCHEMA)
- .concat(DISK_SCHEMA)
+ // .concat(DISK_SCHEMA)
.concat(OWNERSHIP_SCHEMA)
.concat(VM_GROUP_SCHEMA)
.concat(VCENTER_SCHEMA)
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js
index 2cb05baf30..45f10bac8b 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js
@@ -14,7 +14,7 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useMemo, SetStateAction } from 'react'
+import { SetStateAction } from 'react'
import PropTypes from 'prop-types'
import {
@@ -25,11 +25,10 @@ import {
} from 'iconoir-react'
import { Divider, makeStyles } from '@material-ui/core'
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd'
-import { useFormContext } from 'react-hook-form'
import { Translate } from 'client/components/HOC'
import { Action } from 'client/components/Cards/SelectCard'
-import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable'
+import { TAB_ID as STORAGE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage'
import { TAB_ID as NIC_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking'
import { set } from 'client/utils'
import { T } from 'client/constants'
@@ -54,10 +53,40 @@ const useStyles = makeStyles(theme => ({
}))
/**
- * @param {string[]} newBootOrder - New boot order
- * @param {SetStateAction} setFormData - New boot order
+ * @param {string} id - Resource id: 'NIC' or 'DISK'
+ * @param {Array} list - List of resources
+ * @param {object} formData - Form data
+ * @param {SetStateAction} setFormData - React set state action
*/
-export const reorder = (newBootOrder, setFormData) => {
+export const reorderBootAfterRemove = (id, list, formData, setFormData) => {
+ const type = String(id).toLowerCase().replace(/\d+/g, '') // nic | disk
+ const getIndexFromId = id => String(id).toLowerCase().replace(type, '')
+ const idxToRemove = getIndexFromId(id)
+
+ const ids = list
+ .filter(resource => resource.NAME !== id)
+ .map(resource => String(resource.NAME).toLowerCase())
+
+ const newBootOrder = [...formData?.OS?.BOOT?.split(',').filter(Boolean)]
+ .filter(bootId => !bootId.startsWith(type) || ids.includes(bootId))
+ .map(bootId => {
+ if (!bootId.startsWith(type)) return bootId
+
+ const resourceId = getIndexFromId(bootId)
+
+ return resourceId < idxToRemove
+ ? bootId
+ : `${type}${resourceId - 1}`
+ })
+
+ reorder(newBootOrder, setFormData)
+}
+
+/**
+ * @param {string[]} newBootOrder - New boot order
+ * @param {SetStateAction} setFormData - React set state action
+ */
+const reorder = (newBootOrder, setFormData) => {
setFormData(prev => {
const newData = set({ ...prev }, 'extra.OS.BOOT', newBootOrder.join(','))
@@ -67,41 +96,32 @@ export const reorder = (newBootOrder, setFormData) => {
const Booting = ({ data, setFormData }) => {
const classes = useStyles()
- const { watch } = useFormContext()
const bootOrder = data?.OS?.BOOT?.split(',').filter(Boolean) ?? []
- const disks = useMemo(() => {
- const templateSeleted = watch(`${TEMPLATE_ID}[0]`)
- const listOfDisks = [templateSeleted?.TEMPLATE?.DISK ?? []].flat()
-
- return listOfDisks?.map(disk => {
- const { DISK_ID, IMAGE, IMAGE_ID } = disk
- const isVolatile = !IMAGE && !IMAGE_ID
-
- const name = isVolatile
- ? <>`DISK ${DISK_ID}: `>
- : `DISK ${DISK_ID}: ${IMAGE}`
+ const disks = data?.[STORAGE_ID]
+ ?.map((disk, idx) => {
+ const isVolatile = !disk?.IMAGE && !disk?.IMAGE_ID
return {
- ID: `disk${DISK_ID}`,
+ ID: `disk${idx}`,
NAME: (
<>
- {name}
+ {isVolatile
+ ? <>{`${disk?.NAME}: `}>
+ : [disk?.NAME, disk?.IMAGE].filter(Boolean).join(': ')}
>
)
}
- })
- }, [])
+ }) ?? []
const nics = data?.[NIC_ID]
- ?.map((nic, idx) => ({ ...nic, NAME: nic?.NAME ?? `NIC${idx}` }))
?.map((nic, idx) => ({
ID: `nic${idx}`,
NAME: (
<>
- {[nic?.NAME ?? `NIC${idx}`, nic.NETWORK].filter(Boolean).join(': ')}
+ {[nic?.NAME, nic.NETWORK].filter(Boolean).join(': ')}
>
)
})) ?? []
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js
index dcd13b88cd..2e5cfaeb62 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js
@@ -22,6 +22,7 @@ import { WarningCircledOutline as WarningIcon } from 'iconoir-react'
import Tabs from 'client/components/Tabs'
import { SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
+import Storage from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage'
import Networking from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking'
import Placement from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/placement'
import ScheduleAction from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/scheduleAction'
@@ -35,6 +36,10 @@ const Content = ({ data, setFormData }) => {
const { errors } = useFormContext()
const tabs = [
+ {
+ name: 'storage',
+ renderContent: Storage({ data, setFormData })
+ },
{
name: 'network',
renderContent: Networking({ data, setFormData })
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js
index fcdf2677ec..75ce48d1a5 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js
@@ -25,8 +25,9 @@ import { AttachNicForm } from 'client/components/Forms/Vm'
import { Tr, Translate } from 'client/components/HOC'
import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
-import { NIC_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
-import { reorder } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting'
+import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
+import { reorderBootAfterRemove } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting'
+import { stringToBoolean } from 'client/models/Helper'
import { T } from 'client/constants'
const useStyles = makeStyles({
@@ -43,36 +44,21 @@ export const TAB_ID = 'NIC'
const Networking = ({ data, setFormData }) => {
const classes = useStyles()
const nics = data?.[TAB_ID]
- ?.map((nic, idx) => ({ ...nic, NAME: nic?.NAME ?? `NIC${idx}` }))
- const { handleRemove, handleSave } = useListForm({
+ const { handleSetList, handleRemove, handleSave } = useListForm({
parent: EXTRA_ID,
key: TAB_ID,
list: nics,
setList: setFormData,
- getItemId: (item) => item.NAME ?? `NIC${data.length + 1}`,
- addItemId: (item, id) => ({ ...item, NAME: id })
+ getItemId: item => item.NAME,
+ addItemId: (item, _, itemIndex) => ({ ...item, NAME: `${TAB_ID}${itemIndex}` })
})
- const reorderBootOrder = nicId => {
- const getIndexFromNicId = id => String(id).toLowerCase().replace('nic', '')
- const idxToRemove = getIndexFromNicId(nicId)
+ const reorderNics = () => {
+ const diskSchema = EXTRA_SCHEMA.pick([TAB_ID])
+ const { [TAB_ID]: newList } = diskSchema.cast({ [TAB_ID]: data?.[TAB_ID] })
- const nicIds = nics
- .filter(nic => nic.NAME !== nicId)
- .map(nic => String(nic.NAME).toLowerCase())
-
- const newBootOrder = [...data?.OS?.BOOT?.split(',').filter(Boolean)]
- .filter(bootId => !bootId.startsWith('nic') || nicIds.includes(bootId))
- .map(bootId => {
- if (!bootId.startsWith('nic')) return bootId
-
- const nicId = getIndexFromNicId(bootId)
-
- return nicId < idxToRemove ? bootId : `nic${nicId - 1}`
- })
-
- reorder(newBootOrder, setFormData)
+ handleSetList(newList)
}
return (
@@ -88,8 +74,7 @@ const Networking = ({ data, setFormData }) => {
}}
options={[{
form: () => AttachNicForm({ nics }),
- onSubmit: formData =>
- handleSave(NIC_SCHEMA.cast(formData))
+ onSubmit: handleSave
}]}
/>
@@ -103,7 +88,12 @@ const Networking = ({ data, setFormData }) => {
title={[NAME, NETWORK].filter(Boolean).join(' - ')}
subheader={<>
{Object
- .entries({ RDP, SSH, ALIAS: PARENT, EXTERNAL })
+ .entries({
+ RDP: stringToBoolean(RDP),
+ SSH: stringToBoolean(SSH),
+ EXTERNAL: stringToBoolean(EXTERNAL),
+ ALIAS: PARENT
+ })
.map(([k, v]) => v ? `${k}` : '')
.filter(Boolean)
.join(' | ')
@@ -116,7 +106,8 @@ const Networking = ({ data, setFormData }) => {
data-cy={`remove-${NAME}`}
handleClick={() => {
handleRemove(NAME)
- reorderBootOrder(NAME)
+ reorderNics()
+ reorderBootAfterRemove(NAME, nics, data, setFormData)
}}
icon={
}
/>
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js
index 4840939958..4e8576a2f3 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js
@@ -17,7 +17,6 @@
import { array, object, string, lazy } from 'yup'
import { v4 as uuidv4 } from 'uuid'
-import { SCHEMA as NETWORK_SCHEMA } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions/schema'
import { SCHEMA as PUNCTUAL_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/PunctualForm/schema'
import { SCHEMA as RELATIVE_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/RelativeForm/schema'
import { INPUT_TYPES } from 'client/constants'
@@ -65,14 +64,6 @@ export const DS_RANK_FIELD = {
validation: string().trim().notRequired()
}
-export const NIC_SCHEMA = object({
- NAME: string().trim(),
- NETWORK_ID: string().trim(),
- NETWORK: string().trim(),
- NETWORK_UNAME: string().trim(),
- SECURITY_GROUPS: string().trim()
-}).concat(NETWORK_SCHEMA)
-
export const SCHED_ACTION_SCHEMA = lazy(({ TIME } = {}) => {
const isRelative = String(TIME).includes('+')
const schema = isRelative ? RELATIVE_SCHEMA : PUNCTUAL_SCHEMA
@@ -81,7 +72,22 @@ export const SCHED_ACTION_SCHEMA = lazy(({ TIME } = {}) => {
})
export const SCHEMA = object({
- NIC: array(NIC_SCHEMA).ensure(),
+ DISK: array()
+ .ensure()
+ .transform(disks => disks?.map((disk, idx) => ({
+ ...disk,
+ NAME: disk?.NAME?.startsWith('DISK') || !disk?.NAME
+ ? `DISK${idx}`
+ : disk?.NAME
+ }))),
+ NIC: array()
+ .ensure()
+ .transform(nics => nics?.map((nic, idx) => ({
+ ...nic,
+ NAME: nic?.NAME?.startsWith('NIC') || !nic?.NAME
+ ? `NIC${idx}`
+ : nic?.NAME
+ }))),
SCHED_ACTION: array(SCHED_ACTION_SCHEMA).ensure(),
OS: object({
BOOT: string().trim().notRequired()
@@ -92,4 +98,4 @@ export const SCHEMA = object({
DS_REQ_FIELD,
DS_RANK_FIELD
])
-})
+}).noUnknown(false)
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js
new file mode 100644
index 0000000000..2303707f1a
--- /dev/null
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js
@@ -0,0 +1,194 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, 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. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { useMemo } from 'react'
+import PropTypes from 'prop-types'
+import { useFormContext } from 'react-hook-form'
+import { makeStyles } from '@material-ui/core'
+import { Edit, Trash } from 'iconoir-react'
+
+import { useListForm } from 'client/hooks'
+import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
+import SelectCard, { Action } from 'client/components/Cards/SelectCard'
+import { ImageSteps, VolatileSteps } from 'client/components/Forms/Vm'
+import { StatusCircle, StatusChip } from 'client/components/Status'
+import { Tr, Translate } from 'client/components/HOC'
+
+import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable'
+import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
+import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
+import { reorderBootAfterRemove } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting'
+import { getState, getDiskType } from 'client/models/Image'
+import { stringToBoolean } from 'client/models/Helper'
+import { prettyBytes } from 'client/utils'
+import { T } from 'client/constants'
+
+const useStyles = makeStyles({
+ root: {
+ paddingBlock: '1em',
+ display: 'grid',
+ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, auto))',
+ gap: '1em'
+ }
+})
+
+export const TAB_ID = 'DISK'
+
+const Storage = ({ data, setFormData }) => {
+ const classes = useStyles()
+ const { watch } = useFormContext()
+ const { HYPERVISOR } = useMemo(() => watch(`${TEMPLATE_ID}[0]`) ?? {}, [])
+ const disks = data?.[TAB_ID]
+
+ const { handleSetList, handleRemove, handleSave } = useListForm({
+ parent: EXTRA_ID,
+ key: TAB_ID,
+ list: disks,
+ setList: setFormData,
+ getItemId: item => item.NAME,
+ addItemId: (item, _, itemIndex) => ({ ...item, NAME: `${TAB_ID}${itemIndex}` })
+ })
+
+ const reorderDisks = () => {
+ const diskSchema = EXTRA_SCHEMA.pick([TAB_ID])
+ const { [TAB_ID]: newList } = diskSchema.cast({ [TAB_ID]: data?.[TAB_ID] })
+
+ handleSetList(newList)
+ }
+
+ return (
+ <>
+
ImageSteps({ hypervisor: HYPERVISOR }),
+ onSubmit: handleSave
+ },
+ {
+ cy: 'attach-volatile-disk',
+ name: T.Volatile,
+ form: () => VolatileSteps({ hypervisor: HYPERVISOR }),
+ onSubmit: handleSave
+ }
+ ]}
+ />
+
+ {disks?.map(item => {
+ const {
+ NAME,
+ TYPE,
+ IMAGE,
+ IMAGE_ID,
+ IMAGE_STATE,
+ ORIGINAL_SIZE,
+ SIZE = ORIGINAL_SIZE,
+ READONLY,
+ DATASTORE,
+ PERSISTENT
+ } = item
+
+ const isVolatile = !IMAGE && !IMAGE_ID
+ const isPersistent = stringToBoolean(PERSISTENT)
+ const state = !isVolatile && getState({ STATE: IMAGE_STATE })
+ const type = isVolatile ? TYPE : getDiskType(item)
+ const originalSize = +ORIGINAL_SIZE ? prettyBytes(+ORIGINAL_SIZE, 'MB') : '-'
+ const size = prettyBytes(+SIZE, 'MB')
+
+ return (
+
+ {`${NAME} - `}
+
+ >
+ ) : (
+
+
+ {`${NAME}: ${IMAGE}`}
+ {isPersistent && }
+
+ )}
+ subheader={<>
+ {Object
+ .entries({
+ [DATASTORE]: DATASTORE,
+ READONLY: stringToBoolean(READONLY),
+ PERSISTENT: stringToBoolean(PERSISTENT),
+ [isVolatile || ORIGINAL_SIZE === SIZE ? size : `${originalSize}/${size}`]: true,
+ [type]: type
+ })
+ .map(([k, v]) => v ? `${k}` : '')
+ .filter(Boolean)
+ .join(' | ')
+ }
+ >}
+ action={
+ <>
+ }
+ handleClick={() => {
+ handleRemove(NAME)
+ reorderDisks()
+ reorderBootAfterRemove(NAME, disks, data, setFormData)
+ }}
+ icon={}
+ />
+ ,
+ tooltip:
+ }}
+ dialogProps={{
+ title: <>{`: ${NAME}`}>
+ }}
+ options={[{
+ form: () => isVolatile
+ ? VolatileSteps({ hypervisor: HYPERVISOR }, item)
+ : ImageSteps({ hypervisor: HYPERVISOR }, item),
+ onSubmit: newValues => handleSave(newValues, NAME)
+ }]}
+ />
+ >
+ }
+ />
+ )
+ })}
+
+ >
+ )
+}
+
+Storage.propTypes = {
+ data: PropTypes.any,
+ setFormData: PropTypes.func
+}
+
+Storage.displayName = 'Storage'
+
+export default Storage
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js
index 5f7501ffca..8fa507edfa 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js
@@ -15,6 +15,7 @@
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
+import { makeStyles } from '@material-ui/core'
import { useListForm } from 'client/hooks'
import { useVmTemplateApi } from 'client/features/One'
@@ -29,7 +30,14 @@ import { T } from 'client/constants'
export const STEP_ID = 'template'
+const useStyles = makeStyles({
+ body: {
+ gridTemplateColumns: 'repeat(auto-fill, minmax(400px, 1fr))'
+ }
+})
+
const Content = ({ data, setFormData }) => {
+ const classes = useStyles()
const selectedTemplate = data?.[0]
const { getVmTemplate } = useVmTemplateApi()
@@ -62,6 +70,7 @@ const Content = ({ data, setFormData }) => {
singleSelect
onlyGlobalSearch
onlyGlobalSelectedRows
+ classes={classes}
initialState={{ selectedRowIds: { [selectedTemplate?.ID]: true } }}
onSelectedRowsChange={handleSelectedRows}
/>
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 e5ca99d541..dfe51a8633 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
@@ -17,7 +17,7 @@ import VmTemplatesTable, { STEP_ID as TEMPLATE_ID } from 'client/components/Form
import BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration'
import ExtraConfiguration, { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
import { jsonToXml } from 'client/models/Helper'
-import { createSteps, deepmerge } from 'client/utils'
+import { createSteps } from 'client/utils'
const Steps = createSteps(
[VmTemplatesTable, BasicConfiguration, ExtraConfiguration],
@@ -27,7 +27,7 @@ const Steps = createSteps(
[TEMPLATE_ID]: [vmTemplate],
[BASIC_ID]: vmTemplate?.TEMPLATE,
[EXTRA_ID]: vmTemplate?.TEMPLATE
- }, { stripUnknown: true })
+ }, { stripUnknown: true, context: { [TEMPLATE_ID]: [vmTemplate] } })
}),
transformBeforeSubmit: formData => {
const {
@@ -37,8 +37,7 @@ const Steps = createSteps(
} = formData ?? {}
// merge with template disks to get TYPE attribute
- const DISK = deepmerge(templateSelected.TEMPLATE?.DISK, restOfConfig?.DISK)
- const templateXML = jsonToXml({ ...extraTemplate, ...restOfConfig, DISK })
+ const templateXML = jsonToXml({ ...extraTemplate, ...restOfConfig })
const data = { instances, hold, persistent, template: templateXML }
const templates = [...new Array(instances)]
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/index.js
index 43828f9b54..2b2c302b9c 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/index.js
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
+import CloneForm from 'client/components/Forms/VmTemplate/CloneForm'
import InstantiateForm from 'client/components/Forms/VmTemplate/InstantiateForm'
export {
+ CloneForm,
InstantiateForm
}
diff --git a/src/fireedge/src/client/components/Icons/opennebula.js b/src/fireedge/src/client/components/Icons/opennebula.js
index 4af4161754..9a5d8e57d8 100644
--- a/src/fireedge/src/client/components/Icons/opennebula.js
+++ b/src/fireedge/src/client/components/Icons/opennebula.js
@@ -60,26 +60,28 @@ const OpenNebulaLogo = memo(({ width, height, spinner, withText, viewBox, ...pro
)}
{/* --------------- CLOUD ------------------ */}
-
-
-
-
-
+
+
+
+
+
+
+
{withText && (
diff --git a/src/fireedge/src/client/components/Image/index.js b/src/fireedge/src/client/components/Image/index.js
index abfab0a17c..b43b061606 100644
--- a/src/fireedge/src/client/components/Image/index.js
+++ b/src/fireedge/src/client/components/Image/index.js
@@ -47,7 +47,12 @@ const Image = memo(({ src, imageInError, withSources, imgProps }) => {
}
if (error.fail) {
- return
+ return
}
return (
diff --git a/src/fireedge/src/client/components/Tables/Enhanced/index.js b/src/fireedge/src/client/components/Tables/Enhanced/index.js
index f864cc28fe..2b943a6190 100644
--- a/src/fireedge/src/client/components/Tables/Enhanced/index.js
+++ b/src/fireedge/src/client/components/Tables/Enhanced/index.js
@@ -17,6 +17,7 @@
import { useMemo } from 'react'
import PropTypes from 'prop-types'
+import clsx from 'clsx'
import { InfoEmpty } from 'iconoir-react'
import { Box, LinearProgress } from '@material-ui/core'
import {
@@ -54,9 +55,10 @@ const EnhancedTable = ({
pageSize = 10,
RowComponent,
showPageCount,
- singleSelect = false
+ singleSelect = false,
+ classes = {}
}) => {
- const classes = EnhancedTableStyles()
+ const styles = EnhancedTableStyles()
const isFetching = isLoading && data === undefined
@@ -127,8 +129,8 @@ const EnhancedTable = ({
}
return (
-
-
+
+
{/* TOOLBAR */}
{!isFetching && (
+
{page?.length > 0 && (
{isLoading && (
-
+
)}
-
+
{/* FILTERS */}
{!isFetching && (
)}
-
+
{/* NO DATA MESSAGE */}
{!isFetching && page?.length === 0 && (
-
+
@@ -209,6 +211,10 @@ export const EnhancedTableProps = {
fetchMore: PropTypes.func,
getRowId: PropTypes.func,
initialState: PropTypes.object,
+ classes: PropTypes.shape({
+ root: PropTypes.string,
+ body: PropTypes.string
+ }),
isLoading: PropTypes.bool,
onlyGlobalSearch: PropTypes.bool,
onlyGlobalSelectedRows: PropTypes.bool,
diff --git a/src/fireedge/src/client/components/Tables/Enhanced/styles.js b/src/fireedge/src/client/components/Tables/Enhanced/styles.js
index 7439bbecfd..bc6e057123 100644
--- a/src/fireedge/src/client/components/Tables/Enhanced/styles.js
+++ b/src/fireedge/src/client/components/Tables/Enhanced/styles.js
@@ -58,6 +58,7 @@ export default makeStyles(
display: 'grid',
gap: '1em',
gridTemplateColumns: 'minmax(0, 1fr)',
+ // gridTemplateRows: 'repeat(auto-fill, 10em)',
gridAutoRows: 'max-content',
paddingBlock: '0.8em',
'& > [role=row]': {
@@ -68,9 +69,9 @@ export default makeStyles(
fontWeight: typography.fontWeightMedium,
fontSize: '1em',
border: `1px solid ${palette.divider}`,
- borderRadius: 6,
+ borderRadius: '0.5em',
display: 'flex',
- gap: 8,
+ gap: '1em',
'&:hover': {
backgroundColor: palette.action.hover
},
diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/actions.js b/src/fireedge/src/client/components/Tables/VmTemplates/actions.js
index d5b9b73cde..c7707f8469 100644
--- a/src/fireedge/src/client/components/Tables/VmTemplates/actions.js
+++ b/src/fireedge/src/client/components/Tables/VmTemplates/actions.js
@@ -33,6 +33,7 @@ import {
import { useAuth } from 'client/features/Auth'
import { useVmTemplateApi } from 'client/features/One'
+import { CloneForm } from 'client/components/Forms/VmTemplate'
import { createActions } from 'client/components/Tables/Enhanced/Utils'
import { PATH } from 'client/apps/sunstone/routesOne'
import { VM_TEMPLATE_ACTIONS, MARKETPLACE_APP_ACTIONS } from 'client/constants'
@@ -40,7 +41,14 @@ import { VM_TEMPLATE_ACTIONS, MARKETPLACE_APP_ACTIONS } from 'client/constants'
const Actions = () => {
const history = useHistory()
const { view, getResourceView } = useAuth()
- const { getVmTemplate, getVmTemplates, lock, unlock, remove } = useVmTemplateApi()
+ const {
+ getVmTemplate,
+ getVmTemplates,
+ lock,
+ unlock,
+ clone,
+ remove
+ } = useVmTemplateApi()
const vmTemplateActions = useMemo(() => createActions({
filters: getResourceView('VM-TEMPLATE')?.actions,
@@ -106,8 +114,42 @@ const Actions = () => {
label: 'Clone',
tooltip: 'Clone',
selected: true,
- disabled: true,
- options: [{ onSubmit: () => undefined }]
+ dialogProps: {
+ title: rows => {
+ const isMultiple = rows?.length > 1
+ const { ID, NAME } = rows?.[0]?.original
+
+ return [
+ isMultiple ? 'Clone several Templates' : 'Clone Template',
+ !isMultiple && `#${ID} ${NAME}`
+ ].filter(Boolean).join(' - ')
+ }
+ },
+ options: [{
+ form: rows => {
+ const vmTemplates = rows?.map(({ original }) => original)
+ const stepProps = { isMultiple: vmTemplates.length > 1 }
+ const initialValues = { name: `Copy of ${vmTemplates?.[0]?.NAME}` }
+
+ return CloneForm(stepProps, initialValues)
+ },
+ onSubmit: async (formData, rows) => {
+ try {
+ const { prefix } = formData
+
+ const vmTemplates = rows?.map?.(({ original: { ID, NAME } = {} }) => {
+ // overwrite all names with prefix+NAME
+ const formatData = prefix ? { name: `${prefix} ${NAME}` } : {}
+
+ return { id: ID, data: { ...formData, ...formatData } }
+ })
+
+ await Promise.all(vmTemplates.map(({ id, data }) => clone(id, data)))
+ } finally {
+ await getVmTemplates()
+ }
+ }
+ }]
},
{
tooltip: 'Change ownership',
@@ -157,7 +199,7 @@ const Actions = () => {
isConfirmDialog: true,
onSubmit: async (_, rows) => {
const templateIds = rows?.map?.(({ original }) => original?.ID)
- await Promise.all([...new Array(templateIds)].map(id => lock(id)))
+ await Promise.all(templateIds.map(id => lock(id)))
await Promise.all(templateIds.map(id => getVmTemplate(id)))
}
}]
@@ -178,7 +220,7 @@ const Actions = () => {
options: [{
isConfirmDialog: true,
onSubmit: async (_, rows) => {
- const templateIds = [...new Array(rows?.map?.(({ original }) => original?.ID))]
+ const templateIds = rows?.map?.(({ original }) => original?.ID)
await Promise.all(templateIds.map(id => unlock(id)))
await Promise.all(templateIds.map(id => getVmTemplate(id)))
}
@@ -199,7 +241,7 @@ const Actions = () => {
options: [{
isConfirmDialog: true,
onSubmit: async (_, rows) => {
- const templateIds = [...new Array(rows?.map?.(({ original }) => original?.ID))]
+ const templateIds = rows?.map?.(({ original }) => original?.ID)
await Promise.all(templateIds.map(id => remove(id)))
await getVmTemplates()
}
diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js
index ce21cd4a1c..f5ca3471eb 100644
--- a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js
+++ b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js
@@ -16,6 +16,7 @@
/* eslint-disable jsdoc/require-jsdoc */
import { CategoryFilter } from 'client/components/Tables/Enhanced/Utils'
import * as Helper from 'client/models/Helper'
+import { } from 'client/constants'
export default [
{ Header: 'ID', accessor: 'ID', sortType: 'number' },
@@ -24,6 +25,11 @@ export default [
{ Header: 'Group', accessor: 'GNAME' },
{ Header: 'Start Time', accessor: 'REGTIME' },
{ Header: 'Locked', accessor: 'LOCK' },
+ {
+ Header: 'Logo',
+ id: 'LOGO',
+ accessor: row => row?.TEMPLATE?.LOGO
+ },
{
Header: 'Virtual Router',
id: 'VROUTER',
diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/row.js b/src/fireedge/src/client/components/Tables/VmTemplates/row.js
index db62a56aa4..c61c075a7c 100644
--- a/src/fireedge/src/client/components/Tables/VmTemplates/row.js
+++ b/src/fireedge/src/client/components/Tables/VmTemplates/row.js
@@ -14,6 +14,7 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
+import { useMemo } from 'react'
import PropTypes from 'prop-types'
import { User, Group, Lock } from 'iconoir-react'
@@ -21,18 +22,38 @@ import { Typography } from '@material-ui/core'
import { StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
+import Image from 'client/components/Image'
import * as Helper from 'client/models/Helper'
+import { isExternalURL } from 'client/utils'
+import { LOGO_IMAGES_URL } from 'client/constants'
const Row = ({ original, value, ...props }) => {
const classes = rowStyles()
- const { ID, NAME, UNAME, GNAME, REGTIME, LOCK, VROUTER } = value
+ const { ID, NAME, UNAME, GNAME, REGTIME, LOCK, VROUTER, LOGO = '' } = value
+ const [logoSource] = useMemo(() => {
+ const external = isExternalURL(LOGO)
+ const cleanLogoAttribute = String(LOGO).split('/').at(-1)
+ const src = external ? LOGO : `${LOGO_IMAGES_URL}/${cleanLogoAttribute}`
+
+ return [src, external]
+ }, [LOGO])
+
+ const logo = String(LOGO).split('/').at(-1)
const time = Helper.timeFromMilliseconds(+REGTIME)
const timeAgo = `registered ${time.toRelative()}`
return (
+ {logo && (
+
+
+
+ )}
@@ -44,7 +65,7 @@ const Row = ({ original, value, ...props }) => {
-
+
{`#${ID} ${timeAgo}`}
diff --git a/src/fireedge/src/client/components/Tables/styles.js b/src/fireedge/src/client/components/Tables/styles.js
index 107fcc0c09..e14675fce9 100644
--- a/src/fireedge/src/client/components/Tables/styles.js
+++ b/src/fireedge/src/client/components/Tables/styles.js
@@ -31,9 +31,25 @@ export const rowStyles = makeStyles(
flexWrap: 'wrap'
}
},
+ figure: {
+ flexBasis: '20%',
+ paddingTop: '12.5%',
+ overflow: 'hidden',
+ position: 'relative'
+ },
+ image: {
+ top: 0,
+ left: 0,
+ width: '100%',
+ height: '100%',
+ objectFit: 'contain',
+ position: 'absolute',
+ userSelect: 'none'
+ },
main: {
flex: 'auto',
- overflow: 'hidden'
+ overflow: 'hidden',
+ alignSelf: 'center'
},
title: {
color: palette.text.primary,
@@ -52,8 +68,12 @@ export const rowStyles = makeStyles(
color: palette.text.secondary,
marginTop: 4,
display: 'flex',
- gap: 8,
- wordWrap: 'break-word'
+ gap: '0.5em',
+ flexWrap: 'wrap',
+ wordWrap: 'break-word',
+ '& > .full-width': {
+ flexBasis: '100%'
+ }
},
secondary: {
width: '25%',
diff --git a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js
index 75a00b13ee..99e756b308 100644
--- a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js
+++ b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js
@@ -38,11 +38,8 @@ const VmStorageTab = ({ tabProps: { actions } = {} }) => {
const hypervisor = VirtualMachine.getHypervisor(vm)
const actionsAvailable = Helper.getActionsAvailable(actions, hypervisor)
- const handleAttachDisk = async ({ image, advanced, configuration }) => {
- const imageSelected = image?.[0]
- const root = { ...imageSelected, ...advanced, ...configuration }
-
- const template = Helper.jsonToXml({ DISK: root })
+ const handleAttachDisk = async formData => {
+ const template = Helper.jsonToXml({ DISK: formData })
await attachDisk(vm.ID, template)
}
diff --git a/src/fireedge/src/client/constants/image.js b/src/fireedge/src/client/constants/image.js
index dc0c68980e..f0231e6c45 100644
--- a/src/fireedge/src/client/constants/image.js
+++ b/src/fireedge/src/client/constants/image.js
@@ -16,31 +16,51 @@
import * as STATES from 'client/constants/states'
import COLOR from 'client/constants/color'
-/**
- * @enum {(
- * 'OS'|
- * 'CD ROM'|
- * 'DATABLOCK'|
- * 'KERNEL'|
- * 'RAMDISK'|
- * 'CONTEXT'
- * )} Image type
- */
+/** @enum {string} Image type */
+export const IMAGE_TYPES_STR = {
+ OS: 'OS',
+ CDROM: 'CDROM',
+ DATABLOCK: 'DATABLOCK',
+ KERNEL: 'KERNEL',
+ RAMDISK: 'RAMDISK',
+ CONTEXT: 'CONTEXT'
+}
+
+/** @type {IMAGE_TYPES_STR[]} Return the string representation of an Image type */
export const IMAGE_TYPES = [
- 'OS',
- 'CD ROM',
- 'DATABLOCK',
- 'KERNEL',
- 'RAMDISK',
- 'CONTEXT'
+ IMAGE_TYPES_STR.OS,
+ IMAGE_TYPES_STR.CDROM,
+ IMAGE_TYPES_STR.DATABLOCK,
+ IMAGE_TYPES_STR.KERNEL,
+ IMAGE_TYPES_STR.RAMDISK,
+ IMAGE_TYPES_STR.CONTEXT
]
-/** @enum {('FILE'|'CD ROM'|'BLOCK'|'RBD')} Disk type */
+/** @enum {string} Disk type */
+export const DISK_TYPES_STR = {
+ FILE: 'FILE',
+ CDROM: 'CDROM',
+ BLOCK: 'BLOCK',
+ RBD: 'RBD',
+ RBD_CDROM: 'RBD CDROM',
+ GLUSTER: 'GLUSTER',
+ GLUSTER_CDROM: 'GLUSTER CDROM',
+ SHEEPDOG: 'SHEEPDOG',
+ SHEEPDOG_CDROM: 'SHEEPDOG CDROM',
+ ISCSI: 'ISCII'
+}
+/** @enum {DISK_TYPES_STR[]} Return the string representation of a Disk type */
export const DISK_TYPES = [
- 'FILE',
- 'CD ROM',
- 'BLOCK',
- 'RBD'
+ DISK_TYPES_STR.FILE,
+ DISK_TYPES_STR.CDROM,
+ DISK_TYPES_STR.BLOCK,
+ DISK_TYPES_STR.RBD,
+ DISK_TYPES_STR.RBD_CDROM,
+ DISK_TYPES_STR.GLUSTER,
+ DISK_TYPES_STR.GLUSTER_CDROM,
+ DISK_TYPES_STR.SHEEPDOG,
+ DISK_TYPES_STR.SHEEPDOG_CDROM,
+ DISK_TYPES_STR.ISCSI
]
/** @type {STATES.StateInfo[]} Image states */
diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js
index bd6c3c7a2e..2ec55b8c17 100644
--- a/src/fireedge/src/client/constants/index.js
+++ b/src/fireedge/src/client/constants/index.js
@@ -31,6 +31,7 @@ export const WEBSOCKET_URL = `${APP_URL}/websockets`
export const STATIC_FILES_URL = `${APP_URL}/client/assets`
export const IMAGES_URL = `${STATIC_FILES_URL}/images`
+export const LOGO_IMAGES_URL = `${IMAGES_URL}/logos`
export const PROVIDER_IMAGES_URL = `${IMAGES_URL}/providers`
export const PROVISION_IMAGES_URL = `${IMAGES_URL}/provisions`
export const DEFAULT_IMAGE = `${IMAGES_URL}/default.webp`
diff --git a/src/fireedge/src/client/features/One/utils.js b/src/fireedge/src/client/features/One/utils.js
index d8b34bb417..5c878d6ae3 100644
--- a/src/fireedge/src/client/features/One/utils.js
+++ b/src/fireedge/src/client/features/One/utils.js
@@ -61,7 +61,7 @@ export const updateResourceList = (currentList, value) => {
// update if exists in current list, if not add it to list
const updatedList = currentItem
? currentList?.map(item => item?.ID === id ? value : item)
- : [value, currentList]
+ : [value, ...currentList]
return updatedList
}
diff --git a/src/fireedge/src/client/features/One/vmTemplate/actions.js b/src/fireedge/src/client/features/One/vmTemplate/actions.js
index 333d153005..2813e4cf48 100644
--- a/src/fireedge/src/client/features/One/vmTemplate/actions.js
+++ b/src/fireedge/src/client/features/One/vmTemplate/actions.js
@@ -40,4 +40,4 @@ export const changePermissions = createAction(`${TEMPLATE}/chmod`, vmTemplateSer
export const changeOwnership = createAction(`${TEMPLATE}/chown`, vmTemplateService.changeOwnership)
export const rename = createAction(`${TEMPLATE}/rename`, vmTemplateService.rename)
export const lock = createAction(`${TEMPLATE}/lock`, vmTemplateService.lock)
-export const unlock = createAction(`${TEMPLATE}/unlock`, vmTemplateService.lock)
+export const unlock = createAction(`${TEMPLATE}/unlock`, vmTemplateService.unlock)
diff --git a/src/fireedge/src/client/hooks/useListForm.js b/src/fireedge/src/client/hooks/useListForm.js
index 42973b503e..f09b78d6d3 100644
--- a/src/fireedge/src/client/hooks/useListForm.js
+++ b/src/fireedge/src/client/hooks/useListForm.js
@@ -61,7 +61,7 @@ import { set } from 'client/utils'
* @property {SimpleCallback} handleSelect - Add an item to data form list
* @property {SimpleCallback} handleClone - Clones an item and change two attributes
* @property {SimpleCallback} handleEdit - Find the element by id and set value to editing state
- * @property {function(newValues, id)} handleSave - Saves the data from editing state
+ * @property {SaveCallback} handleSave - Saves the data from editing state
*/
// ----------------------------------------------------------
@@ -182,7 +182,7 @@ const useListForm = ({
const index = EXISTS_INDEX(itemIndex) ? itemIndex : list.length
const newList = Object.assign([], [...list],
- { [index]: getItemId(values) ? values : addItemId(values, id, itemIndex) }
+ { [index]: getItemId(values) ? values : addItemId(values, id, index) }
)
handleSetList(newList)
diff --git a/src/fireedge/src/client/models/Image.js b/src/fireedge/src/client/models/Image.js
index fa123f79ab..a20903cd8c 100644
--- a/src/fireedge/src/client/models/Image.js
+++ b/src/fireedge/src/client/models/Image.js
@@ -22,7 +22,8 @@ import { IMAGE_TYPES, DISK_TYPES, IMAGE_STATES, StateInfo } from 'client/constan
* @param {number|string} image.TYPE - Type numeric code
* @returns {IMAGE_TYPES} - Image type
*/
-export const getType = ({ TYPE } = {}) => IMAGE_TYPES[+TYPE]
+export const getType = ({ TYPE } = {}) =>
+ isNaN(+TYPE) ? TYPE : IMAGE_TYPES[+TYPE]
/**
* Returns the image state.
@@ -40,4 +41,5 @@ export const getState = ({ STATE } = {}) => IMAGE_STATES[+STATE]
* @param {number|string} image.DISK_TYPE - Disk type numeric code
* @returns {DISK_TYPES} - Disk type
*/
-export const getDiskType = ({ DISK_TYPE } = {}) => DISK_TYPES[+DISK_TYPE]
+export const getDiskType = ({ DISK_TYPE } = {}) =>
+ isNaN(+DISK_TYPE) ? DISK_TYPE : DISK_TYPES[+DISK_TYPE]
diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js
index 905e63c4da..faab65678f 100644
--- a/src/fireedge/src/client/utils/schema.js
+++ b/src/fireedge/src/client/utils/schema.js
@@ -111,7 +111,7 @@ import { INPUT_TYPES } from 'client/constants'
/**
* @typedef {object} StepsForm
* @property {Step[]} steps - Steps
- * @property {BaseSchema} resolver - Schema
+ * @property {BaseSchema|function():BaseSchema} resolver - Schema
* @property {object} defaultValues - Default values
*/
@@ -347,8 +347,13 @@ export const createForm = (schema, fields, extraParams = {}) =>
const schemaCallback = typeof schema === 'function' ? schema(props) : schema
const fieldsCallback = typeof fields === 'function' ? fields(props) : fields
+ const defaultTransformInitialValue = (values, schema) =>
+ schema.cast(values, { stripUnknown: true })
+
+ const { transformInitialValue = defaultTransformInitialValue } = extraParams
+
const defaultValues = initialValues
- ? schemaCallback.cast(initialValues, { stripUnknown: true })
+ ? transformInitialValue(initialValues, schemaCallback)
: schemaCallback.default()
return {