foreman_fog_proxmox 0.15.1 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/proxmox_compute_resources_helper.rb +1 -1
  3. data/app/helpers/proxmox_form_helper.rb +2 -2
  4. data/app/helpers/proxmox_vm_attrs_helper.rb +131 -0
  5. data/app/helpers/proxmox_vm_interfaces_helper.rb +3 -3
  6. data/app/helpers/proxmox_vm_volumes_helper.rb +3 -3
  7. data/app/models/concerns/fog_extensions/proxmox/node.rb +1 -1
  8. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +3 -3
  9. data/app/models/foreman_fog_proxmox/proxmox_images.rb +5 -0
  10. data/app/models/foreman_fog_proxmox/proxmox_version.rb +1 -1
  11. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +1 -1
  12. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +1 -1
  13. data/app/overrides/compute_resources_vms/form/add_from_profile_to_compute_attributes_form.rb +8 -0
  14. data/app/overrides/compute_resources_vms/form/add_react_component_to_host.rb +25 -0
  15. data/app/overrides/compute_resources_vms/form/update_react_component_to_host_form.rb +25 -0
  16. data/app/views/compute_resources_vms/form/proxmox/_add_react_component_to_host_form.html.erb +5 -0
  17. data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +21 -21
  18. data/app/views/compute_resources_vms/form/proxmox/_update_react_component_to_host_form.html.erb +26 -0
  19. data/config/routes.rb +3 -3
  20. data/lib/foreman_fog_proxmox/version.rb +1 -1
  21. data/package.json +42 -0
  22. data/test/factories/proxmox_factory.rb +7 -7
  23. data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +1 -1
  24. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +6 -6
  25. data/webpack/components/GeneralTabContent.js +107 -0
  26. data/webpack/components/ProxmoxComputeSelectors.js +141 -0
  27. data/webpack/components/ProxmoxContainer/MountPoint.js +91 -0
  28. data/webpack/components/ProxmoxContainer/ProxmoxContainerHardware.js +85 -0
  29. data/webpack/components/ProxmoxContainer/ProxmoxContainerNetwork.js +179 -0
  30. data/webpack/components/ProxmoxContainer/ProxmoxContainerOptions.js +104 -0
  31. data/webpack/components/ProxmoxContainer/ProxmoxContainerStorage.js +194 -0
  32. data/webpack/components/ProxmoxContainer/components/NetworkInterface.js +193 -0
  33. data/webpack/components/ProxmoxServer/ProxmoxServerHardware.js +204 -0
  34. data/webpack/components/ProxmoxServer/ProxmoxServerNetwork.js +161 -0
  35. data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +105 -0
  36. data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +272 -0
  37. data/webpack/components/ProxmoxServer/components/CDRom.js +149 -0
  38. data/webpack/components/ProxmoxServer/components/CPUFlagsModal.js +88 -0
  39. data/webpack/components/ProxmoxServer/components/HardDisk.js +143 -0
  40. data/webpack/components/ProxmoxServer/components/NetworkInterface.js +150 -0
  41. data/webpack/components/ProxmoxStoragesUtils.js +50 -0
  42. data/webpack/components/ProxmoxVmType.js +256 -0
  43. data/webpack/components/ProxmoxVmUtils.js +62 -0
  44. data/webpack/components/common/FormInputs.js +143 -0
  45. data/webpack/global_index.js +15 -0
  46. data/webpack/index.js +7 -0
  47. metadata +49 -21
@@ -0,0 +1,104 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+ import { createStoragesMap, imagesByStorage } from '../ProxmoxStoragesUtils';
5
+ import ProxmoxComputeSelectors from '../ProxmoxComputeSelectors';
6
+ import InputField from '../common/FormInputs';
7
+
8
+ const ProxmoxContainerOptions = ({ options, storages, nodeId }) => {
9
+ const [opts, setOpts] = useState(options);
10
+ const storagesMap = createStoragesMap(storages, 'vztmpl', nodeId);
11
+ const volumesMap = imagesByStorage(storages, nodeId, 'local', 'vztmpl');
12
+ const handleChange = e => {
13
+ const { name, type, checked, value: targetValue } = e.target;
14
+ let value;
15
+ if (type === 'checkbox') {
16
+ value = checked ? '1' : '0';
17
+ } else {
18
+ value = targetValue;
19
+ }
20
+ const updatedKey = Object.keys(opts).find(key => opts[key].name === name);
21
+
22
+ setOpts(prevOpts => ({
23
+ ...prevOpts,
24
+ [updatedKey]: { ...prevOpts[updatedKey], value },
25
+ }));
26
+ };
27
+ return (
28
+ <div>
29
+ <InputField
30
+ name={opts?.ostemplateStorage?.name}
31
+ label={__('Template Storage')}
32
+ value={opts?.ostemplateStorage?.value}
33
+ options={storagesMap}
34
+ type="select"
35
+ onChange={handleChange}
36
+ />
37
+ <InputField
38
+ name={opts?.ostemplateFile?.name}
39
+ label={__('OS Template')}
40
+ required
41
+ options={volumesMap}
42
+ value={opts?.ostemplateFile?.value}
43
+ type="select"
44
+ onChange={handleChange}
45
+ />
46
+ <InputField
47
+ name={opts?.password?.name}
48
+ label={__('Root Password')}
49
+ required
50
+ type="password"
51
+ value={opts?.password?.value}
52
+ onChange={handleChange}
53
+ />
54
+ <InputField
55
+ name={opts?.onboot?.name}
56
+ label={__('Start at boot')}
57
+ type="checkbox"
58
+ value={opts?.onboot?.value}
59
+ checked={opts?.onboot?.value === '1'}
60
+ onChange={handleChange}
61
+ />
62
+ <InputField
63
+ name={opts?.ostype?.name}
64
+ label={__('OS Type')}
65
+ type="select"
66
+ options={ProxmoxComputeSelectors.proxmoxOperatingSystemsMap}
67
+ value={opts?.ostype?.value}
68
+ onChange={handleChange}
69
+ />
70
+ <InputField
71
+ name={opts?.hostname?.name}
72
+ label={__('Hostname')}
73
+ value={opts?.hostname?.value}
74
+ onChange={handleChange}
75
+ />
76
+ <InputField
77
+ name={opts?.nameserver?.name}
78
+ label={__('DNS server')}
79
+ value={opts?.nameserver?.value}
80
+ onChange={handleChange}
81
+ />
82
+ <InputField
83
+ name={opts?.searchdomain?.name}
84
+ label={__('Search Domain')}
85
+ value={opts?.searchdomain?.value}
86
+ onChange={handleChange}
87
+ />
88
+ </div>
89
+ );
90
+ };
91
+
92
+ ProxmoxContainerOptions.propTypes = {
93
+ options: PropTypes.object,
94
+ storages: PropTypes.array,
95
+ nodeId: PropTypes.string,
96
+ };
97
+
98
+ ProxmoxContainerOptions.defaultProps = {
99
+ options: {},
100
+ storages: [],
101
+ nodeId: '',
102
+ };
103
+
104
+ export default ProxmoxContainerOptions;
@@ -0,0 +1,194 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import { Button, Title, Divider, PageSection } from '@patternfly/react-core';
3
+ import { TimesIcon } from '@patternfly/react-icons';
4
+ import { sprintf, translate as __ } from 'foremanReact/common/I18n';
5
+ import PropTypes from 'prop-types';
6
+ import { createStoragesMap } from '../ProxmoxStoragesUtils';
7
+ import InputField from '../common/FormInputs';
8
+ import MountPoint from './MountPoint';
9
+
10
+ const ProxmoxContainerStorage = ({ storage, storages, nodeId, paramScope }) => {
11
+ const initData = {
12
+ id: {
13
+ name: `${paramScope}[volumes_attributes][0][id]`,
14
+ value: 'rootfs',
15
+ },
16
+ device: {
17
+ name: `${paramScope}[volumes_attributes][0][device]`,
18
+ value: '8',
19
+ },
20
+ storage: {
21
+ name: `${paramScope}[volumes_attributes][0][storage]`,
22
+ value: '',
23
+ },
24
+ size: {
25
+ name: `${paramScope}[volumes_attributes][0][size]`,
26
+ value: 8,
27
+ },
28
+ volid: {
29
+ name: `${paramScope}[volumes_attributes][0][volid]`,
30
+ value: null,
31
+ },
32
+ };
33
+ const [rootfs, setRootfs] = useState(initData);
34
+ useEffect(() => {
35
+ if (storage && storage.length > 0) {
36
+ storage.forEach(disk => {
37
+ if (disk.name === 'rootfs') {
38
+ setRootfs(disk.value);
39
+ }
40
+ if (disk.name === 'mount_point') {
41
+ addMountPoint(null, disk.value);
42
+ }
43
+ });
44
+ }
45
+ }, [storage]);
46
+
47
+ const handleChange = e => {
48
+ const { name, value } = e.target;
49
+ const updatedKey = Object.keys(rootfs).find(
50
+ key => rootfs[key].name === name
51
+ );
52
+ const updatedData = {
53
+ ...rootfs,
54
+ [updatedKey]: { ...rootfs[updatedKey], value },
55
+ };
56
+ setRootfs(updatedData);
57
+ };
58
+ const [mountPoints, setMountPoints] = useState([]);
59
+ const [nextId, setNextId] = useState(0);
60
+ const storagesMap = createStoragesMap(storages, null, nodeId);
61
+ const addMountPoint = useCallback(
62
+ (event, initialData = null) => {
63
+ if (event) event.preventDefault();
64
+ const initMP = initialData || {
65
+ id: {
66
+ name: `${paramScope}[volumes_attributes][${nextId}][id]`,
67
+ value: `mp${nextId}`,
68
+ },
69
+ device: {
70
+ name: `${paramScope}[volumes_attributes][${nextId}][device]`,
71
+ value: `${nextId}`,
72
+ },
73
+ storage: {
74
+ name: `${paramScope}[volumes_attributes][${nextId}][storage]`,
75
+ value: '',
76
+ },
77
+ size: {
78
+ name: `${paramScope}[volumes_attributes][${nextId}][size]`,
79
+ value: 8,
80
+ },
81
+ mp: {
82
+ name: `${paramScope}[volumes_attributes][${nextId}][mp]`,
83
+ value: '',
84
+ },
85
+ volid: {
86
+ name: `${paramScope}[volumes_attributes][${nextId}][volid]`,
87
+ value: null,
88
+ },
89
+ };
90
+
91
+ setNextId(prevNextId => {
92
+ const newNextId = prevNextId + 1;
93
+ const newMountPoint = {
94
+ id: newNextId,
95
+ data: initMP,
96
+ storagesMap,
97
+ };
98
+ setMountPoints(prevMountPoints => [...prevMountPoints, newMountPoint]);
99
+ return newNextId;
100
+ });
101
+ },
102
+ [nextId, paramScope, setMountPoints, setNextId, storagesMap]
103
+ );
104
+
105
+ const removeMountPoint = idToRemove => {
106
+ const newMountPoints = mountPoints.filter(
107
+ mountPoint => mountPoint.props.id !== idToRemove
108
+ );
109
+ setMountPoints(newMountPoints);
110
+ };
111
+
112
+ return (
113
+ <div>
114
+ <PageSection padding={{ default: 'noPadding' }}>
115
+ <Title headingLevel="h3">{__('Rootfs')}</Title>
116
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
117
+ <InputField
118
+ name={rootfs?.storage?.name}
119
+ label="Storage"
120
+ type="select"
121
+ value={rootfs?.storage?.value}
122
+ options={storagesMap}
123
+ onChange={handleChange}
124
+ />
125
+ <InputField
126
+ name={rootfs?.size?.name}
127
+ label={__('Size')}
128
+ type="number"
129
+ value={rootfs?.size?.value}
130
+ onChange={handleChange}
131
+ />
132
+ <input
133
+ name={rootfs?.id?.name}
134
+ type="hidden"
135
+ value={rootfs?.id?.value || 'rootfs'}
136
+ onChange={handleChange}
137
+ />
138
+ <input
139
+ name={rootfs?.volid?.name}
140
+ type="hidden"
141
+ value={rootfs?.volid?.value}
142
+ />
143
+ </PageSection>
144
+ <PageSection padding={{ default: 'noPadding' }}>
145
+ <Title headingLevel="h3">Storage</Title>
146
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
147
+ <Button onClick={addMountPoint} variant="secondary">
148
+ {__('Add MountPoint')}
149
+ </Button>
150
+ {mountPoints.map(mountPoint => (
151
+ <div key={mountPoint.id} style={{ position: 'relative' }}>
152
+ <div
153
+ style={{
154
+ marginTop: '10px',
155
+ display: 'flex',
156
+ justifyContent: 'space-between',
157
+ alignItems: 'center',
158
+ }}
159
+ >
160
+ <Title headingLevel="h4">
161
+ {sprintf(__('Mount Point %(mp)s'), { mp: mountPoint.id })}
162
+ </Title>
163
+ <button onClick={() => removeMountPoint(mountPoint.id)}>
164
+ <TimesIcon />
165
+ </button>
166
+ </div>
167
+ <MountPoint
168
+ key={mountPoint.id}
169
+ id={mountPoint.id}
170
+ data={mountPoint.data}
171
+ storagesMap={mountPoint.storagesMap}
172
+ />
173
+ </div>
174
+ ))}
175
+ </PageSection>
176
+ </div>
177
+ );
178
+ };
179
+
180
+ ProxmoxContainerStorage.propTypes = {
181
+ storage: PropTypes.object,
182
+ storages: PropTypes.array,
183
+ nodeId: PropTypes.string,
184
+ paramScope: PropTypes.string,
185
+ };
186
+
187
+ ProxmoxContainerStorage.defaultProps = {
188
+ storage: {},
189
+ storages: [],
190
+ nodeId: '',
191
+ paramScope: '',
192
+ };
193
+
194
+ export default ProxmoxContainerStorage;
@@ -0,0 +1,193 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Divider } from '@patternfly/react-core';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import InputField from '../../common/FormInputs';
6
+
7
+ const NetworkInterface = ({
8
+ id,
9
+ networks,
10
+ bridges,
11
+ data,
12
+ updateNetworkData,
13
+ existingInterfaces,
14
+ }) => {
15
+ const [network, setNetwork] = useState(data);
16
+ const [error, setError] = useState('');
17
+ useEffect(() => {
18
+ const currentNetData = JSON.stringify(network);
19
+ const parentNetData = JSON.stringify(data);
20
+
21
+ if (currentNetData !== parentNetData) {
22
+ updateNetworkData(id, network);
23
+ }
24
+ }, [network, id, data, updateNetworkData]);
25
+ const handleChange = e => {
26
+ const { name, type, checked, value: targetValue } = e.target;
27
+ let value;
28
+ if (type === 'checkbox') {
29
+ value = checked ? '1' : '0';
30
+ } else {
31
+ value = targetValue;
32
+ }
33
+ const updatedKey = Object.keys(network).find(
34
+ key => network[key].name === name
35
+ );
36
+ const updatedData = {
37
+ ...network,
38
+ [updatedKey]: { ...network[updatedKey], value },
39
+ };
40
+ setNetwork(updatedData);
41
+
42
+ if (updatedKey === 'id') {
43
+ const idValue = value;
44
+ if (
45
+ Object.values(existingInterfaces).some(
46
+ net =>
47
+ net.data.id.value === idValue &&
48
+ net.data.id.value !== network.id.value
49
+ )
50
+ ) {
51
+ setError(__('Error: Duplicate ID found.'));
52
+ return;
53
+ }
54
+ setError('');
55
+ }
56
+ if (updatedKey === 'name') {
57
+ const idValue = value;
58
+ if (
59
+ Object.values(existingInterfaces).some(
60
+ net =>
61
+ net.data.id.value === idValue &&
62
+ net.data.id.value !== network.id.value
63
+ )
64
+ ) {
65
+ setError(__('Error: Duplicate Name found.'));
66
+ } else {
67
+ setError('');
68
+ }
69
+ }
70
+ };
71
+ const bridgesMap = bridges.map(bridge => ({
72
+ value: bridge.iface,
73
+ label: bridge.iface,
74
+ }));
75
+
76
+ return (
77
+ <div style={{ position: 'relative' }}>
78
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
79
+ <InputField
80
+ name={network?.id?.name}
81
+ label={__('Identifier')}
82
+ info={__('net[n] with n integer >= 0, e.g. net0')}
83
+ type="text"
84
+ value={network?.id?.value}
85
+ onChange={handleChange}
86
+ error={error}
87
+ />
88
+ <InputField
89
+ name={network?.name?.name}
90
+ label={__('Name')}
91
+ info={__('eth[n] with n integer >= 0, e.g. eth0')}
92
+ type="text"
93
+ value={network?.name?.value}
94
+ onChange={handleChange}
95
+ error={error}
96
+ />
97
+ <InputField
98
+ name={network?.bridge?.name}
99
+ label={__('Bridge')}
100
+ type="select"
101
+ options={bridgesMap}
102
+ value={network?.bridge?.value}
103
+ onChange={handleChange}
104
+ />
105
+ <InputField
106
+ name={network?.dhcp?.name}
107
+ label={__('DHCP IPv4')}
108
+ type="checkbox"
109
+ value={network?.dhcp?.value}
110
+ checked={network?.dhcp?.value === '1'}
111
+ onChange={handleChange}
112
+ />
113
+ <InputField
114
+ name={network?.cidr?.name}
115
+ label={__('CIDR IPv4')}
116
+ info={__('integer within [0..32]')}
117
+ type="text"
118
+ value={network?.cidr?.value}
119
+ onChange={handleChange}
120
+ />
121
+ <InputField
122
+ name={network?.gw?.name}
123
+ label={__('Gateway IPv4')}
124
+ type="text"
125
+ value={network?.gw?.value}
126
+ onChange={handleChange}
127
+ />
128
+ <InputField
129
+ name={network?.dhcp6?.name}
130
+ label={__('DHCP IPv6')}
131
+ type="checkbox"
132
+ value={network?.dhcp6?.value}
133
+ checked={network?.dhcp6?.value === '1'}
134
+ onChange={handleChange}
135
+ />
136
+ <InputField
137
+ name={network?.cidr6?.name}
138
+ label={__('CIDR IPv6')}
139
+ info={__('integer within [0..128]')}
140
+ type="text"
141
+ value={network?.cidr6?.value}
142
+ onChange={handleChange}
143
+ />
144
+ <InputField
145
+ name={network?.gw6?.name}
146
+ label={__('Gateway IPv6')}
147
+ type="text"
148
+ value={network?.gw6?.value}
149
+ onChange={handleChange}
150
+ />
151
+ <InputField
152
+ name={network?.tag?.name}
153
+ label={__('VLAN Tag')}
154
+ type="text"
155
+ value={network?.tag?.value}
156
+ onChange={handleChange}
157
+ />
158
+ <InputField
159
+ name={network?.rate?.name}
160
+ label={__('Rate limit')}
161
+ type="text"
162
+ value={network?.rate?.value}
163
+ onChange={handleChange}
164
+ />
165
+ <InputField
166
+ name={network?.firewall?.name}
167
+ label={__('Firewall')}
168
+ type="checkbox"
169
+ value={network?.firewall?.value}
170
+ checked={network?.firewall?.value === '1'}
171
+ onChange={handleChange}
172
+ />
173
+ </div>
174
+ );
175
+ };
176
+
177
+ NetworkInterface.propTypes = {
178
+ id: PropTypes.number.isRequired,
179
+ networks: PropTypes.array,
180
+ bridges: PropTypes.array,
181
+ data: PropTypes.object,
182
+ updateNetworkData: PropTypes.func,
183
+ existingInterfaces: PropTypes.array,
184
+ };
185
+
186
+ NetworkInterface.defaultProps = {
187
+ networks: [],
188
+ bridges: [],
189
+ data: {},
190
+ updateNetworkData: Function.prototype,
191
+ existingInterfaces: [],
192
+ };
193
+ export default NetworkInterface;
@@ -0,0 +1,204 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, Title, Divider, PageSection } from '@patternfly/react-core';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+ import PropTypes from 'prop-types';
5
+ import InputField from '../common/FormInputs';
6
+ import ProxmoxComputeSelectors from '../ProxmoxComputeSelectors';
7
+ import CPUFlagsModal from './components/CPUFlagsModal';
8
+
9
+ const cpuFlagNames = [
10
+ 'md_clear',
11
+ 'pcid',
12
+ 'spec_ctrl',
13
+ 'ssbd',
14
+ 'ibpb',
15
+ 'virt_ssbd',
16
+ 'amd_ssbd',
17
+ 'amd_no_ssb',
18
+ 'pdpe1gb',
19
+ 'hv_tlbflush',
20
+ 'hv_evmcs',
21
+ 'aes',
22
+ ];
23
+
24
+ const cpuFlagDescriptions = {
25
+ md_clear: __(
26
+ 'Required to let the guest OS know if MDS is mitigated correctly'
27
+ ),
28
+ pcid: __(
29
+ 'Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs'
30
+ ),
31
+ spec_ctrl: __('Allows improved Spectre mitigation with Intel CPUs'),
32
+ ssbd: __('Protection for "Speculative Store Bypass" for Intel models'),
33
+ ibpb: __('Allows improved Spectre mitigation with AMD CPUs'),
34
+ virt_ssbd: __(
35
+ 'Basis for "Speculative Store Bypass" protection for AMD models'
36
+ ),
37
+ amd_ssbd: __(
38
+ 'Improves Spectre mitigation performance with AMD CPUs, best used with "virt-ssbd"'
39
+ ),
40
+ amd_no_ssb: __(
41
+ 'Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs'
42
+ ),
43
+ pdpe1gb: __('Allow guest OS to use 1GB size pages, if host HW supports it'),
44
+ hv_tlbflush: __(
45
+ 'Improve performance in overcommitted Windows guests. May lead to guest bluescreens on old CPUs.'
46
+ ),
47
+ hv_evmcs: __(
48
+ 'Improve performance for nested virtualization. Only supported on Intel CPUs.'
49
+ ),
50
+ aes: __('Activate AES instruction set for HW instruction'),
51
+ };
52
+
53
+ const filterAndAddDescriptions = hardware =>
54
+ Object.keys(hardware)
55
+ .filter(key => cpuFlagNames.includes(key))
56
+ .reduce((acc, key) => {
57
+ acc[key] = {
58
+ ...hardware[key],
59
+ description: cpuFlagDescriptions[key] || '',
60
+ label: key,
61
+ };
62
+ return acc;
63
+ }, {});
64
+
65
+ const ProxmoxServerHardware = ({ hardware }) => {
66
+ const [hw, setHw] = useState(hardware);
67
+ const [isModalOpen, setIsModalOpen] = useState(false);
68
+
69
+ const handleChange = e => {
70
+ const { name, type, checked, value: targetValue } = e.target;
71
+ let value;
72
+ if (type === 'checkbox') {
73
+ value = checked ? '1' : '0';
74
+ } else {
75
+ value = targetValue;
76
+ }
77
+ const updatedKey = Object.keys(hw).find(key => hw[key].name === name);
78
+
79
+ setHw(prevHw => ({
80
+ ...prevHw,
81
+ [updatedKey]: { ...prevHw[updatedKey], value },
82
+ }));
83
+ };
84
+
85
+ const handleModalToggle = _event => {
86
+ setIsModalOpen(prevIsModalOpen => !prevIsModalOpen);
87
+ };
88
+
89
+ const cpuFlags = filterAndAddDescriptions(hw);
90
+
91
+ return (
92
+ <div>
93
+ <PageSection padding={{ default: 'noPadding' }}>
94
+ <Title headingLevel="h3">{__('CPU')}</Title>
95
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
96
+ <InputField
97
+ name={hw?.cpuType?.name}
98
+ label={__('Type')}
99
+ type="select"
100
+ value={hw?.cpuType?.value}
101
+ options={ProxmoxComputeSelectors.proxmoxCpusMap}
102
+ onChange={handleChange}
103
+ />
104
+ <InputField
105
+ name={hw?.sockets?.name}
106
+ label={__('Sockets')}
107
+ type="number"
108
+ value={hw?.sockets?.value}
109
+ onChange={handleChange}
110
+ />
111
+ <InputField
112
+ name={hw?.cores?.name}
113
+ label={__('Cores')}
114
+ type="number"
115
+ value={hw?.cores?.value}
116
+ onChange={handleChange}
117
+ />
118
+ <InputField
119
+ name={hw?.vcpus?.name}
120
+ label={__('VCPUs')}
121
+ type="number"
122
+ value={hw?.vcpus?.value}
123
+ onChange={handleChange}
124
+ />
125
+ <InputField
126
+ name={hw?.cpulimit?.name}
127
+ label={__('CPU limit')}
128
+ type="number"
129
+ value={hw?.cpulimit?.value}
130
+ onChange={handleChange}
131
+ />
132
+ <InputField
133
+ name={hw?.cpuunits?.name}
134
+ label={__('CPU units')}
135
+ type="number"
136
+ value={hw?.cpuunits?.value}
137
+ onChange={handleChange}
138
+ />
139
+ <InputField
140
+ name={hw?.numa?.name}
141
+ label={__('Enable NUMA')}
142
+ type="checkbox"
143
+ value={hw?.numa?.value}
144
+ checked={hw?.numa?.value === '1'}
145
+ onChange={handleChange}
146
+ />
147
+ <div style={{ marginLeft: '5%', display: 'inline-block' }}>
148
+ <Button variant="link" onClick={handleModalToggle}>
149
+ {__('Extra CPU Flags')}
150
+ </Button>
151
+ </div>
152
+ <CPUFlagsModal
153
+ isOpen={isModalOpen}
154
+ onClose={handleModalToggle}
155
+ flags={cpuFlags}
156
+ handleChange={handleChange}
157
+ />
158
+ {Object.keys(cpuFlags).map(key => (
159
+ <input
160
+ key={hw[key].name}
161
+ name={hw[key].name}
162
+ type="hidden"
163
+ value={hw[key].value}
164
+ />
165
+ ))}
166
+ </PageSection>
167
+ <PageSection padding={{ default: 'noPadding' }}>
168
+ <Title headingLevel="h3">{__('Memory')}</Title>
169
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
170
+ <InputField
171
+ name={hw?.memory?.name}
172
+ label={__('Memory (MB)')}
173
+ type="text"
174
+ value={hw?.memory?.value}
175
+ onChange={handleChange}
176
+ />
177
+ <InputField
178
+ name={hw?.balloon?.name}
179
+ label={__('Minimum Memory (MB)')}
180
+ type="text"
181
+ value={hw?.balloon?.value}
182
+ onChange={handleChange}
183
+ />
184
+ <InputField
185
+ name={hw?.shares?.name}
186
+ label={__('Shares (MB)')}
187
+ type="text"
188
+ value={hw?.shares?.value}
189
+ onChange={handleChange}
190
+ />
191
+ </PageSection>
192
+ </div>
193
+ );
194
+ };
195
+
196
+ ProxmoxServerHardware.propTypes = {
197
+ hardware: PropTypes.object,
198
+ };
199
+
200
+ ProxmoxServerHardware.defaultProps = {
201
+ hardware: {},
202
+ };
203
+
204
+ export default ProxmoxServerHardware;