diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js
index 833db6fcb4..7c779b5b9b 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/kernelSchema.js
@@ -50,7 +50,7 @@ export const KERNEL_DS = {
?.sort((a, b) => {
const compareOptions = { numeric: true, ignorePunctuation: true }
- return a.value.localeCompare(b.value, undefined, compareOptions)
+ return a.text.localeCompare(b.text, undefined, compareOptions)
})
},
validation: kernelValidation.when(
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js
index 51d3a9564d..c0bd639df6 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/booting/ramdiskSchema.js
@@ -50,7 +50,7 @@ export const RAMDISK_DS = {
?.sort((a, b) => {
const compareOptions = { numeric: true, ignorePunctuation: true }
- return a.value.localeCompare(b.value, undefined, compareOptions)
+ return a.text.localeCompare(b.text, undefined, compareOptions)
})
},
validation: ramdiskValidation.when(
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/index.js
index 617b199fa8..1b8a28d6fc 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/index.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/index.js
@@ -22,13 +22,15 @@ import { FormWithSchema } from 'client/components/Forms'
import { STEP_ID as EXTRA_ID, TabType } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration'
import InputsSection, { SECTION_ID as INPUT_ID } from './inputsSection'
-import { INPUT_OUTPUT_FIELDS, INPUTS_FIELDS } from './schema'
+import PciDevicesSection, { SECTION_ID as PCI_ID } from './pciDevicesSection'
+import { INPUT_OUTPUT_FIELDS, INPUTS_FIELDS, PCI_FIELDS } from './schema'
import { T } from 'client/constants'
-export const TAB_ID = ['GRAPHICS', INPUT_ID]
+export const TAB_ID = ['GRAPHICS', INPUT_ID, PCI_ID]
const InputOutput = ({ hypervisor }) => {
const inputsFields = useMemo(() => INPUTS_FIELDS(hypervisor), [hypervisor])
+ const pciDevicesFields = useMemo(() => PCI_FIELDS(hypervisor), [hypervisor])
return (
{
{inputsFields.length > 0 && (
)}
+ {pciDevicesFields.length > 0 && (
+
+ )}
)
}
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSchema.js
new file mode 100644
index 0000000000..301e53de14
--- /dev/null
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSchema.js
@@ -0,0 +1,92 @@
+/* ------------------------------------------------------------------------- *
+ * 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 { string, array, object, ObjectSchema, ArraySchema } from 'yup'
+
+import { useHost } from 'client/features/One'
+import { getPciDevices } from 'client/models/Host'
+import { Field, arrayToOptions, filterFieldsByHypervisor, getValidationFromFields } from 'client/utils'
+import { T, INPUT_TYPES, HYPERVISORS } from 'client/constants'
+
+const { vcenter, lxc, firecracker } = HYPERVISORS
+
+const transformPciToString = (pciDevice = {}) => {
+ const { DEVICE = '', VENDOR = '', CLASS = '' } = pciDevice
+ return [DEVICE, VENDOR, CLASS].join(',')
+}
+
+const getPciAttributes = (pciDevice = '') => {
+ const [DEVICE, VENDOR, CLASS] = pciDevice.split(',')
+ return { DEVICE, VENDOR, CLASS }
+}
+
+/** @type {Field} Name PCI device field */
+const DEVICE_NAME = {
+ name: 'DEVICE_NAME',
+ label: T.DeviceName,
+ notOnHypervisors: [vcenter, lxc, firecracker],
+ type: INPUT_TYPES.SELECT,
+ values: () => {
+ const hosts = useHost()
+ const pciDevices = hosts.map(getPciDevices).flat()
+
+ return arrayToOptions(pciDevices, {
+ getText: ({ DEVICE_NAME } = {}) => DEVICE_NAME,
+ getValue: transformPciToString
+ })
+ },
+ validation: string().trim().notRequired(),
+ grid: { sm: 12, md: 3 }
+}
+
+/** @type {Field} Common field properties */
+const commonFieldProps = name => ({
+ name,
+ notOnHypervisors: [vcenter, lxc, firecracker],
+ type: INPUT_TYPES.TEXT,
+ dependOf: DEVICE_NAME.name,
+ watcher: pciDevice => {
+ if (pciDevice) {
+ const { [name]: attribute } = getPciAttributes(pciDevice)
+
+ return attribute
+ }
+ },
+ validation: string().trim().required(),
+ fieldProps: { disabled: true },
+ grid: { xs: 12, sm: 4, md: 3 }
+})
+
+/** @type {Field} PCI device field */
+const DEVICE = { label: T.Device, ...commonFieldProps('DEVICE') }
+
+/** @type {Field} PCI device field */
+const VENDOR = { label: T.Vendor, ...commonFieldProps('VENDOR') }
+
+/** @type {Field} PCI device field */
+const CLASS = { label: T.Class, ...commonFieldProps('CLASS') }
+
+/**
+ * @param {string} [hypervisor] - VM hypervisor
+ * @returns {Field[]} List of Graphic inputs fields
+ */
+export const PCI_FIELDS = (hypervisor) =>
+ filterFieldsByHypervisor([DEVICE_NAME, DEVICE, VENDOR, CLASS], hypervisor)
+
+/** @type {ObjectSchema} PCI devices object schema */
+export const PCI_SCHEMA = object(getValidationFromFields([DEVICE, VENDOR, CLASS]))
+
+/** @type {ArraySchema} PCI devices schema */
+export const PCI_DEVICES_SCHEMA = array(PCI_SCHEMA).ensure()
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSection.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSection.js
new file mode 100644
index 0000000000..f4d339dc82
--- /dev/null
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/pciDevicesSection.js
@@ -0,0 +1,122 @@
+/* ------------------------------------------------------------------------- *
+ * 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 { JSXElementConstructor, useMemo } from 'react'
+import PropTypes from 'prop-types'
+import { Stack, FormControl, Divider, Button, IconButton } from '@mui/material'
+import List from '@mui/material/List'
+import ListItem from '@mui/material/ListItem'
+import ListItemText from '@mui/material/ListItemText'
+import { DeleteCircledOutline, AddCircledOutline } from 'iconoir-react'
+import { useFieldArray, useForm, FormProvider } from 'react-hook-form'
+import { yupResolver } from '@hookform/resolvers/yup'
+
+import { useHost } from 'client/features/One'
+import { FormWithSchema, Legend } from 'client/components/Forms'
+import { Translate } from 'client/components/HOC'
+import { getPciDevices } from 'client/models/Host'
+
+import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration'
+import { PCI_SCHEMA } from './schema'
+import { T } from 'client/constants'
+
+export const SECTION_ID = 'PCI'
+
+/**
+ * @param {object} props - Props
+ * @param {Array} props.fields - Fields
+ * @returns {JSXElementConstructor} - Inputs section
+ */
+const PciDevicesSection = ({ fields }) => {
+ const hosts = useHost()
+ const pciDevicesAvailable = useMemo(() => hosts.map(getPciDevices).flat(), [hosts.length])
+
+ const { fields: pciDevices, append, remove } = useFieldArray({
+ name: `${EXTRA_ID}.${SECTION_ID}`
+ })
+
+ const methods = useForm({
+ defaultValues: PCI_SCHEMA.default(),
+ resolver: yupResolver(PCI_SCHEMA)
+ })
+
+ const onSubmit = newInput => {
+ append(newInput)
+ methods.reset()
+ }
+
+ return (
+
+
+
+
+
+ }
+ sx={{ mt: '1em' }}
+ >
+
+
+
+
+
+
+ {pciDevices?.map(({ id, DEVICE, VENDOR, CLASS }, index) => {
+ const deviceName = pciDevicesAvailable
+ .find(pciDevice => pciDevice?.DEVICE === DEVICE)?.DEVICE_NAME
+
+ return (
+ remove(index)}>
+
+
+ }
+ sx={{ '&:hover': { bgcolor: 'action.hover' } }}
+ >
+
+
+ )
+ })}
+
+
+ )
+}
+
+PciDevicesSection.propTypes = {
+ fields: PropTypes.array
+}
+
+PciDevicesSection.displayName = 'PciDevicesSection'
+
+export default PciDevicesSection
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/schema.js
index 3f86c41791..0098e85c09 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/schema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/schema.js
@@ -17,6 +17,7 @@ import { object, ObjectSchema } from 'yup'
import { GRAPHICS_FIELDS } from './graphicsSchema'
import { INPUTS_SCHEMA } from './inputsSchema'
+import { PCI_DEVICES_SCHEMA } from './pciDevicesSchema'
import { Field, getObjectSchemaFromFields } from 'client/utils'
/**
@@ -31,10 +32,12 @@ export const INPUT_OUTPUT_FIELDS = hypervisor =>
* @returns {ObjectSchema} I/O schema
*/
export const SCHEMA = hypervisor => object({
- INPUT: INPUTS_SCHEMA
+ INPUT: INPUTS_SCHEMA,
+ PCI: PCI_DEVICES_SCHEMA
}).concat(getObjectSchemaFromFields([
...GRAPHICS_FIELDS(hypervisor)
]))
export * from './graphicsSchema'
export * from './inputsSchema'
+export * from './pciDevicesSchema'
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 6aaddd4fd5..5cffd44051 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,7 +28,7 @@ export const VM_GROUP_FIELD = {
const vmGroups = useVmGroup()
return vmGroups
- ?.map(({ ID, NAME }) => ({ text: `#${ID} ${NAME}`, value: ID }))
+ ?.map(({ ID, NAME }) => ({ text: `#${ID} ${NAME}`, value: String(ID) }))
?.sort((a, b) => {
const compareOptions = { numeric: true, ignorePunctuation: true }
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/vmGroupSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/vmGroupSchema.js
index 8f59e3811e..0e8fb361ea 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/vmGroupSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/vmGroupSchema.js
@@ -27,7 +27,7 @@ export const VM_GROUP_FIELD = {
const vmGroups = useVmGroup()
return vmGroups
- ?.map(({ ID, NAME }) => ({ text: `#${ID} ${NAME}`, value: ID }))
+ ?.map(({ ID, NAME }) => ({ text: `#${ID} ${NAME}`, value: String(ID) }))
?.sort((a, b) => {
const compareOptions = { numeric: true, ignorePunctuation: true }
diff --git a/src/fireedge/src/client/constants/host.js b/src/fireedge/src/client/constants/host.js
index 308b1f8d7c..a177a0b0b6 100644
--- a/src/fireedge/src/client/constants/host.js
+++ b/src/fireedge/src/client/constants/host.js
@@ -16,6 +16,22 @@
import * as STATES from 'client/constants/states'
import COLOR from 'client/constants/color'
+/**
+ * @typedef {object} PciDevice - PCI device
+ * @property {string} DOMAIN - PCI address domain
+ * @property {string} BUS - PCI address bus
+ * @property {string} SLOT - PCI address slot
+ * @property {string} FUNCTION - PCI address function
+ * @property {string} ADDRESS - PCI address, bus, slot and function
+ * @property {string} DEVICE - Id of PCI device
+ * @property {string} CLASS - Id of PCI device class
+ * @property {string} VENDOR - Id of PCI device vendor
+ * @property {string} VMID - Id using this device, -1 if free
+ * @property {string} [DEVICE_NAME] - Name of PCI device
+ * @property {string} [VENDOR_NAME] - Name of PCI device vendor
+ * @property {string} [CLASS_NAME] - Name of PCI device class
+ */
+
/** @type {STATES.StateInfo[]} Host states */
export const HOST_STATES = [
{
diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js
index 81e148ee11..1a262e94b5 100644
--- a/src/fireedge/src/client/constants/translates.js
+++ b/src/fireedge/src/client/constants/translates.js
@@ -473,6 +473,11 @@ module.exports = {
/* VM Template schema - Input/Output */
InputOrOutput: 'Input / Output',
Inputs: 'Inputs',
+ PciDevices: 'PCI Devices',
+ DeviceName: 'Device name',
+ Device: 'Device',
+ Vendor: 'Vendor',
+ Class: 'Class',
/* VM Template schema - Input/Output - graphics */
Graphics: 'Graphics',
VMRC: 'VMRC',
diff --git a/src/fireedge/src/client/models/Host.js b/src/fireedge/src/client/models/Host.js
index a155f90dca..6c12e4ef5c 100644
--- a/src/fireedge/src/client/models/Host.js
+++ b/src/fireedge/src/client/models/Host.js
@@ -14,7 +14,7 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { prettyBytes } from 'client/utils'
-import { DEFAULT_CPU_MODELS, HOST_STATES, HYPERVISORS, StateInfo } from 'client/constants'
+import { DEFAULT_CPU_MODELS, HOST_STATES, HYPERVISORS, PciDevice, StateInfo } from 'client/constants'
/**
* Returns information about the host state.
@@ -79,6 +79,15 @@ export const getHugepageSizes = host => {
.flat()
}
+/**
+ * Returns list of PCI devices from the host.
+ *
+ * @param {object} host - Host
+ * @returns {PciDevice[]} List of PCI devices from resource
+ */
+export const getPciDevices = host =>
+ [host?.HOST_SHARE?.PCI_DEVICES?.PCI ?? []].flat().filter(Boolean)
+
/**
* Returns list of KVM CPU Models available from the host pool.
*