foreman_fog_proxmox 0.15.0 → 0.16.0

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm.js +54 -33
  3. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_server.js +25 -5
  4. data/app/assets/stylesheets/foreman_fog_proxmox/accordion.css +21 -0
  5. data/app/helpers/proxmox_compute_resources_helper.rb +1 -1
  6. data/app/helpers/proxmox_form_helper.rb +2 -2
  7. data/app/helpers/proxmox_vm_attrs_helper.rb +131 -0
  8. data/app/helpers/proxmox_vm_cloudinit_helper.rb +1 -1
  9. data/app/helpers/proxmox_vm_interfaces_helper.rb +3 -3
  10. data/app/helpers/proxmox_vm_volumes_helper.rb +3 -3
  11. data/app/models/concerns/fog_extensions/proxmox/node.rb +1 -1
  12. data/app/models/foreman_fog_proxmox/proxmox.rb +1 -1
  13. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +3 -3
  14. data/app/models/foreman_fog_proxmox/proxmox_console.rb +1 -1
  15. data/app/models/foreman_fog_proxmox/proxmox_images.rb +5 -0
  16. data/app/models/foreman_fog_proxmox/proxmox_version.rb +1 -1
  17. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +5 -2
  18. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +1 -1
  19. data/app/overrides/compute_resources_vms/form/add_from_profile_to_compute_attributes_form.rb +8 -0
  20. data/app/overrides/compute_resources_vms/form/add_react_component_to_host.rb +25 -0
  21. data/app/overrides/compute_resources_vms/form/update_react_component_to_host_form.rb +25 -0
  22. data/app/views/compute_resources_vms/form/proxmox/_add_react_component_to_host_form.html.erb +5 -0
  23. data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +21 -21
  24. data/app/views/compute_resources_vms/form/proxmox/_update_react_component_to_host_form.html.erb +26 -0
  25. data/app/views/compute_resources_vms/form/proxmox/container/_config.html.erb +17 -13
  26. data/app/views/compute_resources_vms/form/proxmox/server/_config.html.erb +20 -20
  27. data/app/views/compute_resources_vms/form/proxmox/server/_volume_hard_disk.html.erb +4 -1
  28. data/config/routes.rb +3 -3
  29. data/lib/foreman_fog_proxmox/version.rb +1 -1
  30. data/package.json +42 -0
  31. data/test/factories/proxmox_factory.rb +7 -7
  32. data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +1 -1
  33. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +6 -6
  34. data/webpack/components/GeneralTabContent.js +107 -0
  35. data/webpack/components/ProxmoxComputeSelectors.js +141 -0
  36. data/webpack/components/ProxmoxContainer/MountPoint.js +91 -0
  37. data/webpack/components/ProxmoxContainer/ProxmoxContainerHardware.js +85 -0
  38. data/webpack/components/ProxmoxContainer/ProxmoxContainerNetwork.js +179 -0
  39. data/webpack/components/ProxmoxContainer/ProxmoxContainerOptions.js +104 -0
  40. data/webpack/components/ProxmoxContainer/ProxmoxContainerStorage.js +194 -0
  41. data/webpack/components/ProxmoxContainer/components/NetworkInterface.js +193 -0
  42. data/webpack/components/ProxmoxServer/ProxmoxServerHardware.js +204 -0
  43. data/webpack/components/ProxmoxServer/ProxmoxServerNetwork.js +161 -0
  44. data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +105 -0
  45. data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +272 -0
  46. data/webpack/components/ProxmoxServer/components/CDRom.js +149 -0
  47. data/webpack/components/ProxmoxServer/components/CPUFlagsModal.js +88 -0
  48. data/webpack/components/ProxmoxServer/components/HardDisk.js +143 -0
  49. data/webpack/components/ProxmoxServer/components/NetworkInterface.js +150 -0
  50. data/webpack/components/ProxmoxStoragesUtils.js +50 -0
  51. data/webpack/components/ProxmoxVmType.js +256 -0
  52. data/webpack/components/ProxmoxVmUtils.js +62 -0
  53. data/webpack/components/common/FormInputs.js +143 -0
  54. data/webpack/global_index.js +15 -0
  55. data/webpack/index.js +7 -0
  56. metadata +50 -21
@@ -0,0 +1,150 @@
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
+ import ProxmoxComputeSelectors from '../../ProxmoxComputeSelectors';
7
+
8
+ const NetworkInterface = ({
9
+ id,
10
+ networks,
11
+ bridges,
12
+ data,
13
+ updateNetworkData,
14
+ existingInterfaces,
15
+ }) => {
16
+ const [network, setNetwork] = useState(data);
17
+ const [error, setError] = useState('');
18
+ useEffect(() => {
19
+ const currentNetData = JSON.stringify(network);
20
+ const parentNetData = JSON.stringify(data);
21
+
22
+ if (currentNetData !== parentNetData) {
23
+ updateNetworkData(id, network);
24
+ }
25
+ }, [network, id, data, updateNetworkData]);
26
+
27
+ const handleChange = e => {
28
+ const { name, type, checked, value: targetValue } = e.target;
29
+ let value;
30
+ if (type === 'checkbox') {
31
+ value = checked ? '1' : '0';
32
+ } else {
33
+ value = targetValue;
34
+ }
35
+ const updatedKey = Object.keys(network).find(
36
+ key => network[key].name === name
37
+ );
38
+ const updatedData = {
39
+ ...network,
40
+ [updatedKey]: { ...network[updatedKey], value },
41
+ };
42
+ setNetwork(updatedData);
43
+
44
+ if (updatedKey === 'id') {
45
+ const idValue = value;
46
+ if (
47
+ Object.values(existingInterfaces).some(
48
+ net =>
49
+ net.data.id.value === idValue &&
50
+ net.data.id.value !== network.id.value
51
+ )
52
+ ) {
53
+ setError(__('Error: Duplicate ID found.'));
54
+ return;
55
+ }
56
+ setError('');
57
+ }
58
+ };
59
+ const bridgesMap = bridges.map(bridge => ({
60
+ value: bridge.iface,
61
+ label: bridge.iface,
62
+ }));
63
+
64
+ return (
65
+ <div style={{ position: 'relative' }}>
66
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
67
+ <InputField
68
+ name={network?.id?.name}
69
+ label={__('Identifier')}
70
+ info={__('net[n] with n integer >= 0, e.g. net0')}
71
+ type="text"
72
+ value={network?.id?.value}
73
+ onChange={handleChange}
74
+ error={error}
75
+ />
76
+ <InputField
77
+ name={network?.model?.name}
78
+ label={__('Card')}
79
+ type="select"
80
+ options={ProxmoxComputeSelectors.proxmoxNetworkcardsMap}
81
+ value={network?.model?.value}
82
+ onChange={handleChange}
83
+ />
84
+ <InputField
85
+ name={network?.bridge?.name}
86
+ label={__('Bridge')}
87
+ type="select"
88
+ options={bridgesMap}
89
+ value={network?.bridge?.value}
90
+ onChange={handleChange}
91
+ />
92
+ <InputField
93
+ name={network?.tag?.name}
94
+ label={__('VLAN Tag')}
95
+ type="text"
96
+ value={network?.tag?.value}
97
+ onChange={handleChange}
98
+ />
99
+ <InputField
100
+ name={network?.rate?.name}
101
+ label={__('Rate limit')}
102
+ type="text"
103
+ value={network?.rate?.value}
104
+ onChange={handleChange}
105
+ />
106
+ <InputField
107
+ name={network?.queues?.name}
108
+ label={__('Multiqueue')}
109
+ type="text"
110
+ value={network?.queues?.value}
111
+ onChange={handleChange}
112
+ />
113
+ <InputField
114
+ name={network?.firewall?.name}
115
+ label={__('Firewall')}
116
+ type="checkbox"
117
+ value={network?.firewall?.value}
118
+ checked={network?.firewall?.value === '1'}
119
+ onChange={handleChange}
120
+ />
121
+ <InputField
122
+ name={network?.linkDown?.name}
123
+ label={__('Disconnect')}
124
+ type="checkbox"
125
+ value={network?.linkDown?.value}
126
+ checked={network?.linkDown?.value === '1'}
127
+ onChange={handleChange}
128
+ />
129
+ </div>
130
+ );
131
+ };
132
+
133
+ NetworkInterface.propTypes = {
134
+ id: PropTypes.number.isRequired,
135
+ networks: PropTypes.array,
136
+ bridges: PropTypes.array,
137
+ data: PropTypes.object,
138
+ updateNetworkData: PropTypes.func,
139
+ existingInterfaces: PropTypes.array,
140
+ };
141
+
142
+ NetworkInterface.defaultProps = {
143
+ networks: [],
144
+ bridges: [],
145
+ data: {},
146
+ updateNetworkData: Function.prototype,
147
+ existingInterfaces: [],
148
+ };
149
+
150
+ export default NetworkInterface;
@@ -0,0 +1,50 @@
1
+ import { sprintf, translate as __ } from 'foremanReact/common/I18n';
2
+
3
+ const humanSize = size => {
4
+ const i = Math.floor(Math.log(size) / Math.log(1024));
5
+ return `${(size / 1000 ** i).toFixed(2) * 1} ${
6
+ ['B', 'kB', 'MB', 'GB', 'TB'][i]
7
+ }`;
8
+ };
9
+
10
+ export const createStoragesMap = (
11
+ storages,
12
+ filterContent = null,
13
+ nodeId = null
14
+ ) =>
15
+ storages
16
+ .filter(st => {
17
+ const contentMatch = filterContent
18
+ ? st.content.includes(filterContent)
19
+ : true;
20
+ const nodeMatch = nodeId ? st.node_id === nodeId : true;
21
+ return contentMatch && nodeMatch;
22
+ })
23
+ .map(st => ({
24
+ value: st.storage,
25
+ label: sprintf(
26
+ __('%(name)s (free: %(free)s, used: %(used)s, total: %(total)s)'),
27
+ {
28
+ name: st.storage,
29
+ free: humanSize(st.avail),
30
+ used: humanSize(st.used),
31
+ total: humanSize(st.total),
32
+ }
33
+ ),
34
+ }));
35
+
36
+ export const imagesByStorage = (storages, nodeId, storageId, type = 'iso') => {
37
+ const storage = storages.find(
38
+ st => st.node_id === nodeId && st.storage === storageId
39
+ );
40
+
41
+ const filteredVolumes = storage.volumes
42
+ .filter(volume => volume.content.includes(type))
43
+ .sort((a, b) => a.volid.localeCompare(b.volid))
44
+ .map(volume => ({
45
+ value: volume.volid,
46
+ label: volume.volid,
47
+ }));
48
+
49
+ return filteredVolumes;
50
+ };
@@ -0,0 +1,256 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ PageSection,
4
+ Divider,
5
+ Tabs,
6
+ Tab,
7
+ TabTitleText,
8
+ } from '@patternfly/react-core';
9
+ import { translate as __ } from 'foremanReact/common/I18n';
10
+ import PropTypes from 'prop-types';
11
+ import { networkSelected } from './ProxmoxVmUtils';
12
+ import ProxmoxComputeSelectors from './ProxmoxComputeSelectors';
13
+ import ProxmoxServerStorage from './ProxmoxServer/ProxmoxServerStorage';
14
+ import ProxmoxServerOptions from './ProxmoxServer/ProxmoxServerOptions';
15
+ import ProxmoxServerNetwork from './ProxmoxServer/ProxmoxServerNetwork';
16
+ import ProxmoxServerHardware from './ProxmoxServer/ProxmoxServerHardware';
17
+ import ProxmoxContainerNetwork from './ProxmoxContainer/ProxmoxContainerNetwork';
18
+ import ProxmoxContainerOptions from './ProxmoxContainer/ProxmoxContainerOptions';
19
+ import ProxmoxContainerStorage from './ProxmoxContainer/ProxmoxContainerStorage';
20
+ import ProxmoxContainerHardware from './ProxmoxContainer/ProxmoxContainerHardware';
21
+ import InputField from './common/FormInputs';
22
+ import GeneralTabContent from './GeneralTabContent';
23
+
24
+ const ProxmoxVmType = ({
25
+ vmAttrs,
26
+ nodes,
27
+ images,
28
+ pools,
29
+ fromProfile,
30
+ newVm,
31
+ storages,
32
+ bridges,
33
+ registerComp,
34
+ untemplatable,
35
+ }) => {
36
+ const nodesMap = nodes
37
+ ? nodes.map(node => ({ value: node.node, label: node.node }))
38
+ : [];
39
+ const imagesMap = images
40
+ ? [
41
+ { value: '', label: '' },
42
+ ...images.map(image => ({
43
+ value: image.uuid,
44
+ label: image.name,
45
+ })),
46
+ ]
47
+ : [];
48
+ const poolsMap = pools
49
+ ? [
50
+ { value: '', label: '' },
51
+ ...pools.map(pool => ({
52
+ value: pool.poolid,
53
+ label: pool.poolid,
54
+ })),
55
+ ]
56
+ : [];
57
+ const [activeTabKey, setActiveTabKey] = useState(0);
58
+ const handleTabClick = (event, tabIndex) => {
59
+ setActiveTabKey(tabIndex);
60
+ };
61
+ const [general, setGeneral] = useState(vmAttrs);
62
+ const paramScope = fromProfile
63
+ ? 'compute_attribute[vm_attrs]'
64
+ : 'host[compute_attributes]';
65
+ const [filteredBridges, setFilteredBridges] = useState([]);
66
+ useEffect(() => {
67
+ if (!registerComp && !fromProfile) {
68
+ networkSelected(general?.type?.value);
69
+ }
70
+ }, [general?.type?.value]);
71
+
72
+ useEffect(() => {
73
+ if (!registerComp) {
74
+ const filtered = bridges.filter(
75
+ bridge => bridge.node_id === general?.nodeId?.value
76
+ );
77
+ setFilteredBridges(filtered);
78
+ }
79
+ }, [general?.nodeId?.value, bridges]);
80
+ if (registerComp) {
81
+ return null;
82
+ }
83
+ const componentMap = {
84
+ qemu: {
85
+ options: <ProxmoxServerOptions options={vmAttrs} />,
86
+ hardware: <ProxmoxServerHardware hardware={vmAttrs} />,
87
+ network: (
88
+ <ProxmoxServerNetwork
89
+ network={vmAttrs?.interfaces || {}}
90
+ bridges={filteredBridges}
91
+ paramScope={paramScope}
92
+ />
93
+ ),
94
+ storage: (
95
+ <ProxmoxServerStorage
96
+ storage={vmAttrs?.disks || {}}
97
+ storages={storages}
98
+ nodeId={general?.nodeId?.value}
99
+ paramScope={paramScope}
100
+ />
101
+ ),
102
+ },
103
+ lxc: {
104
+ options: (
105
+ <ProxmoxContainerOptions
106
+ options={vmAttrs}
107
+ storages={storages}
108
+ paramScope={paramScope}
109
+ nodeId={general?.nodeId?.value}
110
+ />
111
+ ),
112
+ hardware: <ProxmoxContainerHardware hardware={vmAttrs} />,
113
+ network: (
114
+ <ProxmoxContainerNetwork
115
+ network={vmAttrs?.interfaces || {}}
116
+ bridges={filteredBridges}
117
+ paramScope={paramScope}
118
+ />
119
+ ),
120
+ storage: (
121
+ <ProxmoxContainerStorage
122
+ storage={vmAttrs?.disks || {}}
123
+ storages={storages}
124
+ nodeId={general?.nodeId?.value}
125
+ paramScope={paramScope}
126
+ />
127
+ ),
128
+ },
129
+ };
130
+
131
+ const handleChange = e => {
132
+ const { name, type, checked, value: targetValue } = e.target;
133
+ let value;
134
+ if (type === 'checkbox') {
135
+ value = checked ? '1' : '0';
136
+ } else {
137
+ value = targetValue;
138
+ }
139
+ const updatedKey = Object.keys(general).find(
140
+ key => general[key].name === name
141
+ );
142
+
143
+ setGeneral(prevGeneral => ({
144
+ ...prevGeneral,
145
+ [updatedKey]: { ...prevGeneral[updatedKey], value },
146
+ }));
147
+ };
148
+
149
+ return (
150
+ <div>
151
+ <InputField
152
+ name={general?.type?.name}
153
+ label={__('Type')}
154
+ required
155
+ onChange={handleChange}
156
+ value={general?.type?.value}
157
+ options={ProxmoxComputeSelectors.proxmoxTypesMap}
158
+ disabled={!newVm}
159
+ type="select"
160
+ />
161
+ <Tabs
162
+ activeKey={activeTabKey}
163
+ onSelect={handleTabClick}
164
+ aria-label="Options tabs"
165
+ role="region"
166
+ >
167
+ <Tab
168
+ eventKey={0}
169
+ title={<TabTitleText>{__('General')}</TabTitleText>}
170
+ aria-label="Default content - general"
171
+ >
172
+ <GeneralTabContent
173
+ general={general}
174
+ fromProfile={fromProfile}
175
+ newVm={newVm}
176
+ nodesMap={nodesMap}
177
+ poolsMap={poolsMap}
178
+ imagesMap={imagesMap}
179
+ handleChange={handleChange}
180
+ untemplatable={untemplatable}
181
+ />
182
+ </Tab>
183
+ <Tab
184
+ eventKey={1}
185
+ title={<TabTitleText>{__('Advanced Options')}</TabTitleText>}
186
+ aria-label="advanced options"
187
+ >
188
+ <PageSection padding={{ default: 'noPadding' }}>
189
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
190
+ {componentMap[general?.type?.value]?.options}
191
+ </PageSection>
192
+ </Tab>
193
+ <Tab
194
+ eventKey={2}
195
+ title={<TabTitleText>{__('Hardware')}</TabTitleText>}
196
+ aria-label="hardware"
197
+ >
198
+ <PageSection padding={{ default: 'noPadding' }}>
199
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
200
+ {componentMap[general?.type?.value]?.hardware}
201
+ </PageSection>
202
+ </Tab>
203
+ {fromProfile && (
204
+ <Tab
205
+ eventKey={3}
206
+ title={<TabTitleText>{__('Network Interfaces')}</TabTitleText>}
207
+ aria-label="Network interface"
208
+ >
209
+ <PageSection padding={{ default: 'noPadding' }}>
210
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
211
+ {componentMap[general?.type?.value]?.network}
212
+ </PageSection>
213
+ </Tab>
214
+ )}
215
+ <Tab
216
+ eventKey={4}
217
+ title={<TabTitleText>{__('Storage')}</TabTitleText>}
218
+ aria-label="storage"
219
+ >
220
+ <PageSection padding={{ default: 'noPadding' }}>
221
+ <Divider component="li" style={{ marginBottom: '2rem' }} />
222
+ {componentMap[general?.type?.value]?.storage}
223
+ </PageSection>
224
+ </Tab>
225
+ </Tabs>
226
+ </div>
227
+ );
228
+ };
229
+
230
+ ProxmoxVmType.propTypes = {
231
+ vmAttrs: PropTypes.object,
232
+ nodes: PropTypes.array,
233
+ images: PropTypes.array,
234
+ pools: PropTypes.array,
235
+ fromProfile: PropTypes.bool,
236
+ newVm: PropTypes.bool,
237
+ storages: PropTypes.array,
238
+ bridges: PropTypes.array,
239
+ registerComp: PropTypes.bool,
240
+ untemplatable: PropTypes.bool,
241
+ };
242
+
243
+ ProxmoxVmType.defaultProps = {
244
+ vmAttrs: {},
245
+ nodes: [],
246
+ images: [],
247
+ pools: [],
248
+ fromProfile: false,
249
+ newVm: false,
250
+ storages: [],
251
+ bridges: [],
252
+ registerComp: false,
253
+ untemplatable: false,
254
+ };
255
+
256
+ export default ProxmoxVmType;
@@ -0,0 +1,62 @@
1
+ import $ from 'jquery';
2
+
3
+ function networkSelected(value) {
4
+ const fieldSets = [];
5
+ fieldSets.push({
6
+ id: 'network',
7
+ toggle: true,
8
+ newVm: true,
9
+ selected: value,
10
+ });
11
+ fieldSets.forEach(toggleFieldsets);
12
+ return false;
13
+ }
14
+
15
+ function enableFieldSet(fieldSetId, fieldSet) {
16
+ if (fieldSet.toggle && fieldSet.newVm) {
17
+ getFieldSetById(fieldSetId, fieldSet).show();
18
+ }
19
+ getFieldSetById(fieldSetId, fieldSet).removeAttr('disabled');
20
+ getInputHiddenById(fieldSetId).removeAttr('disabled');
21
+ }
22
+
23
+ function disableFieldSet(fieldSetId, fieldSet) {
24
+ if (fieldSet.toggle && fieldSet.newVm) {
25
+ getFieldSetById(fieldSetId, fieldSet).hide();
26
+ }
27
+ getFieldSetById(fieldSetId, fieldSet).attr('disabled', 'disabled');
28
+ getInputHiddenById(fieldSetId).attr('disabled', 'disabled');
29
+ }
30
+
31
+ function toggleFieldSet(fieldSetId, fieldSet, type1, type2) {
32
+ type1 === type2
33
+ ? enableFieldSet(fieldSetId, fieldSet)
34
+ : disableFieldSet(fieldSetId, fieldSet);
35
+ }
36
+
37
+ function getInputHiddenById(volumeId) {
38
+ return $(`div[id^='${volumeId}_volumes'] + input:hidden`);
39
+ }
40
+
41
+ function getFieldSetById(fieldSetId, fieldSet) {
42
+ return $(`fieldset[id^='${fieldSetId}_${fieldSet.id}']`);
43
+ }
44
+
45
+ function getFieldSets(type) {
46
+ return type === 'qemu' ? ['server'] : ['container'];
47
+ }
48
+
49
+ function toggleFieldsets(fieldSet) {
50
+ const removableInputHidden = $(
51
+ `div.removable-item[style='display: none;'] + input:hidden`
52
+ );
53
+ removableInputHidden.attr('disabled', 'disabled');
54
+ ['qemu', 'lxc'].forEach(type => {
55
+ getFieldSets(type).forEach(fieldSetId => {
56
+ toggleFieldSet(fieldSetId, fieldSet, fieldSet.selected, type);
57
+ });
58
+ });
59
+ }
60
+
61
+ window.networkSelected = networkSelected;
62
+ export { networkSelected };
@@ -0,0 +1,143 @@
1
+ import React, { Fragment } from 'react';
2
+ import { FieldLevelHelp } from 'patternfly-react';
3
+ import PropTypes from 'prop-types';
4
+ import CommonForm from 'foremanReact/components/common/forms/CommonForm';
5
+
6
+ const InputField = ({
7
+ name,
8
+ label,
9
+ info,
10
+ value,
11
+ onChange,
12
+ required,
13
+ type,
14
+ disabled,
15
+ options,
16
+ checked,
17
+ error,
18
+ }) => {
19
+ const renderOptions = opts =>
20
+ opts.map(option => <option value={option.value}>{option.label}</option>);
21
+
22
+ let renderComponent;
23
+
24
+ switch (type) {
25
+ case 'textarea':
26
+ renderComponent = (
27
+ <textarea
28
+ name={name}
29
+ className="form-control"
30
+ rows="3"
31
+ cols="50"
32
+ value={value}
33
+ onChange={onChange}
34
+ />
35
+ );
36
+ break;
37
+ case 'select':
38
+ renderComponent = (
39
+ <select
40
+ disabled={disabled}
41
+ name={name}
42
+ className="form-control"
43
+ value={value}
44
+ onChange={onChange}
45
+ >
46
+ {renderOptions(options)}
47
+ </select>
48
+ );
49
+ break;
50
+ case 'checkbox':
51
+ renderComponent = (
52
+ <input
53
+ name={name}
54
+ type={type}
55
+ className=""
56
+ value={value}
57
+ checked={checked}
58
+ onChange={onChange}
59
+ disabled={disabled}
60
+ />
61
+ );
62
+ break;
63
+ default:
64
+ renderComponent = (
65
+ <input
66
+ name={name}
67
+ type={type}
68
+ className="form-control"
69
+ value={value}
70
+ onChange={onChange}
71
+ disabled={disabled}
72
+ />
73
+ );
74
+ break;
75
+ }
76
+
77
+ return (
78
+ <CommonForm
79
+ label={label}
80
+ required={required}
81
+ className="common-textInput"
82
+ tooltipHelp={
83
+ info && (
84
+ <FieldLevelHelp
85
+ buttonClass="field-help"
86
+ content={<Fragment>{info}</Fragment>}
87
+ />
88
+ )
89
+ }
90
+ >
91
+ {renderComponent}
92
+ {error && (
93
+ <div style={{ color: 'red', marginTop: '0.5rem' }}>{error}</div>
94
+ )}
95
+ </CommonForm>
96
+ );
97
+ };
98
+
99
+ InputField.propTypes = {
100
+ name: PropTypes.string,
101
+ label: PropTypes.string,
102
+ info: PropTypes.string,
103
+ value: PropTypes.oneOfType([
104
+ PropTypes.string,
105
+ PropTypes.number,
106
+ PropTypes.bool,
107
+ ]),
108
+ onChange: PropTypes.func.isRequired,
109
+ required: PropTypes.bool,
110
+ type: PropTypes.oneOf([
111
+ 'text',
112
+ 'number',
113
+ 'password',
114
+ 'textarea',
115
+ 'select',
116
+ 'checkbox',
117
+ ]),
118
+ disabled: PropTypes.bool,
119
+ checked: PropTypes.bool,
120
+ options: PropTypes.arrayOf(
121
+ PropTypes.shape({
122
+ label: PropTypes.string.isRequired,
123
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
124
+ .isRequired,
125
+ })
126
+ ),
127
+ error: PropTypes.string,
128
+ };
129
+
130
+ InputField.defaultProps = {
131
+ name: '',
132
+ label: '',
133
+ info: undefined,
134
+ value: '',
135
+ required: false,
136
+ type: 'text',
137
+ disabled: false,
138
+ checked: false,
139
+ options: [],
140
+ error: '',
141
+ };
142
+
143
+ export default InputField;
@@ -0,0 +1,15 @@
1
+ // Placeholder to initialize routes (compare foreman_ansible)
2
+
3
+ /*
4
+ import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
5
+ import { registerReducer } from 'foremanReact/common/MountingService';
6
+ import { registerRoutes } from 'foremanReact/routes/RoutingService';
7
+ import Routes from './src/Router/routes'
8
+
9
+ register client routes
10
+ registerRoutes('PluginTemplate', Routes);
11
+
12
+ register fills for extending foreman core
13
+ http://foreman.surge.sh/?path=/docs/introduction-slot-and-fill--page
14
+ addGlobalFill('<slotId>', '<fillId>', <div key='plugin-template-example' />, 300);
15
+ */
data/webpack/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import componentRegistry from 'foremanReact/components/componentRegistry';
2
+ import ProxmoxVmType from './components/ProxmoxVmType';
3
+
4
+ componentRegistry.register({
5
+ name: 'ProxmoxVmType',
6
+ type: ProxmoxVmType,
7
+ });