foreman_acd 0.0.6 → 0.2.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.
- checksums.yaml +4 -4
- data/app/controllers/foreman_acd/api/v2/app_definitions_controller.rb +1 -2
- data/app/controllers/foreman_acd/app_definitions_controller.rb +29 -1
- data/app/controllers/foreman_acd/app_instances_controller.rb +70 -21
- data/app/controllers/foreman_acd/concerns/app_definition_parameters.rb +1 -1
- data/app/controllers/foreman_acd/concerns/app_instance_parameters.rb +1 -1
- data/app/controllers/ui_acd_controller.rb +0 -1
- data/app/models/foreman_acd/app_definition.rb +0 -1
- data/app/views/foreman_acd/app_definitions/_form.html.erb +6 -14
- data/app/views/foreman_acd/app_definitions/import.html.erb +18 -0
- data/app/views/foreman_acd/app_definitions/index.html.erb +9 -5
- data/app/views/foreman_acd/app_instances/_form.html.erb +5 -5
- data/app/views/foreman_acd/app_instances/deploy.html.erb +19 -0
- data/app/views/foreman_acd/app_instances/index.html.erb +6 -5
- data/app/views/foreman_acd/app_instances/report.html.erb +19 -0
- data/app/views/ui_acd/app_definition.json.rabl +1 -1
- data/config/routes.rb +7 -0
- data/db/migrate/20190610202252_create_app_definitions.rb +1 -3
- data/db/migrate/20190625140305_create_app_instances.rb +1 -1
- data/lib/foreman_acd/plugin.rb +19 -2
- data/lib/foreman_acd/version.rb +1 -1
- data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +261 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinition.scss +1 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +211 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +9 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +147 -0
- data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +6 -0
- data/webpack/components/ApplicationDefinition/index.js +29 -0
- data/webpack/components/ApplicationInstance/ApplicationInstance.js +342 -0
- data/webpack/components/ApplicationInstance/ApplicationInstance.scss +11 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +210 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +12 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +223 -0
- data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +8 -0
- data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +49 -0
- data/webpack/components/ApplicationInstance/components/Service.js +30 -0
- data/webpack/components/ApplicationInstance/components/ServiceCounter.js +37 -0
- data/webpack/components/ApplicationInstance/index.js +33 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +155 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.scss +27 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +86 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +4 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +52 -0
- data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +5 -0
- data/webpack/components/ApplicationInstanceReport/components/ReportViewer.js +26 -0
- data/webpack/components/ApplicationInstanceReport/index.js +27 -0
- data/webpack/components/ParameterSelection/ParameterSelection.js +65 -161
- data/webpack/components/ParameterSelection/ParameterSelection.scss +9 -0
- data/webpack/components/ParameterSelection/ParameterSelectionActions.js +42 -71
- data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +12 -19
- data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +3 -3
- data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +76 -75
- data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -6
- data/webpack/components/ParameterSelection/__fixtures__/parameterSelection.fixtures.js +12 -21
- data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +1 -1
- data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionReducer.fixtures.js +3 -45
- data/webpack/components/ParameterSelection/__tests__/ParameterSelection.test.js +20 -0
- data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +22 -46
- data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -6
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +40 -265
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +11 -96
- data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +3 -9
- data/webpack/components/ParameterSelection/index.js +4 -6
- data/webpack/components/common/AddTableEntry.js +30 -0
- data/webpack/components/common/DeleteTableEntry.js +39 -0
- data/webpack/components/common/ExtSelect.js +43 -0
- data/webpack/components/common/RailsData.js +27 -0
- data/webpack/components/common/__tests__/AddTableEntry.test.js +26 -0
- data/webpack/components/common/__tests__/DeleteTableEntry.test.js +29 -0
- data/webpack/components/common/__tests__/ExtSelect.test.js +38 -0
- data/webpack/components/common/__tests__/RailsData.test.js +16 -0
- data/webpack/components/common/__tests__/__snapshots__/AddParameter.test.js.snap +35 -0
- data/webpack/components/common/__tests__/__snapshots__/AddTableEntry.test.js.snap +35 -0
- data/webpack/components/common/__tests__/__snapshots__/DeleteParameter.test.js.snap +41 -0
- data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +41 -0
- data/webpack/components/common/__tests__/__snapshots__/ExtSelect.test.js.snap +18 -0
- data/webpack/components/common/__tests__/__snapshots__/RailsData.test.js.snap +10 -0
- data/webpack/helper.js +20 -0
- data/webpack/index.js +6 -0
- data/webpack/reducer.js +40 -3
- metadata +47 -46
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const APPLICATION_DEFINITION_INIT = 'INIT_APPLICATION_DEFINITION_INIT';
|
|
2
|
+
export const APPLICATION_DEFINITION_SERVICE_DELETE = 'APPLICATION_DEFINITION_SERVICE_DELETE';
|
|
3
|
+
export const APPLICATION_DEFINITION_SERVICE_ADD = 'APPLICATION_DEFINITION_SERVICE_ADD';
|
|
4
|
+
export const APPLICATION_DEFINITION_SERVICE_EDIT_ACTIVATE = 'APPLICATION_DEFINITION_SERVICE_EDIT_ACTIVATE';
|
|
5
|
+
export const APPLICATION_DEFINITION_SERVICE_EDIT_CONFIRM = 'APPLICATION_DEFINITION_SERVICE_EDIT_CONFIRM';
|
|
6
|
+
export const APPLICATION_DEFINITION_SERVICE_EDIT_CHANGE = 'APPLICATION_DEFINITION_SERVICE_EDIT_CHANGE';
|
|
7
|
+
export const APPLICATION_DEFINITION_SERVICE_EDIT_CANCEL = 'APPLICATION_DEFINITION_SERVICE_EDIT_CANCEL';
|
|
8
|
+
export const APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_OPEN = 'APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_OPEN';
|
|
9
|
+
export const APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_CLOSE = 'APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_CLOSE';
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Immutable from 'seamless-immutable';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cloneDeep,
|
|
5
|
+
findIndex,
|
|
6
|
+
findLastIndex,
|
|
7
|
+
} from 'lodash';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
APPLICATION_DEFINITION_INIT,
|
|
11
|
+
APPLICATION_DEFINITION_SERVICE_DELETE,
|
|
12
|
+
APPLICATION_DEFINITION_SERVICE_ADD,
|
|
13
|
+
APPLICATION_DEFINITION_SERVICE_EDIT_ACTIVATE,
|
|
14
|
+
APPLICATION_DEFINITION_SERVICE_EDIT_CONFIRM,
|
|
15
|
+
APPLICATION_DEFINITION_SERVICE_EDIT_CHANGE,
|
|
16
|
+
APPLICATION_DEFINITION_SERVICE_EDIT_CANCEL,
|
|
17
|
+
APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_OPEN,
|
|
18
|
+
APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_CLOSE,
|
|
19
|
+
} from './ApplicationDefinitionConstants';
|
|
20
|
+
|
|
21
|
+
export const initialState = Immutable({
|
|
22
|
+
name: false,
|
|
23
|
+
error: { errorMsg: '', status: '', statusText: '' },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const applicationDefinitionConf = (state = initialState, action) => {
|
|
27
|
+
const { payload } = action;
|
|
28
|
+
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case APPLICATION_DEFINITION_INIT: {
|
|
31
|
+
return state.merge(payload);
|
|
32
|
+
}
|
|
33
|
+
case APPLICATION_DEFINITION_SERVICE_ADD: {
|
|
34
|
+
let services = [];
|
|
35
|
+
let index = 1;
|
|
36
|
+
|
|
37
|
+
if ('services' in state && state.services !== undefined && state.services.length > 0) {
|
|
38
|
+
services = cloneDeep(state.services);
|
|
39
|
+
index = Math.max(...services.map(e => e.id)) + 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const newRow = {id: index, name: "", description: '', hostgroup: '', minCount: '', maxCount: '', parameters: [], newEntry: true };
|
|
43
|
+
newRow.backup = cloneDeep(newRow)
|
|
44
|
+
services.push(newRow);
|
|
45
|
+
|
|
46
|
+
return state.merge({
|
|
47
|
+
editMode: true,
|
|
48
|
+
services: services
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
case APPLICATION_DEFINITION_SERVICE_DELETE: {
|
|
52
|
+
const services = state.services.filter(v => v.id !== payload.rowData.id);
|
|
53
|
+
return state.merge({
|
|
54
|
+
services: services,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
case APPLICATION_DEFINITION_SERVICE_EDIT_ACTIVATE: {
|
|
58
|
+
const services = cloneDeep(state.services);
|
|
59
|
+
const index = findIndex(services, { id: payload.rowData.id });
|
|
60
|
+
|
|
61
|
+
services[index].backup = cloneDeep(services[index]);
|
|
62
|
+
|
|
63
|
+
return state.merge({
|
|
64
|
+
editMode: true,
|
|
65
|
+
services: services
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
case APPLICATION_DEFINITION_SERVICE_EDIT_CONFIRM: {
|
|
69
|
+
const services = cloneDeep(state.services);
|
|
70
|
+
const index = findIndex(services, { id: payload.rowData.id });
|
|
71
|
+
|
|
72
|
+
delete services[index].backup;
|
|
73
|
+
delete services[index].newEntry;
|
|
74
|
+
|
|
75
|
+
return state.merge({
|
|
76
|
+
editMode: false,
|
|
77
|
+
services: services
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
case APPLICATION_DEFINITION_SERVICE_EDIT_CHANGE: {
|
|
81
|
+
const services = cloneDeep(state.services);
|
|
82
|
+
const index = findIndex(services, { id: payload.rowData.id });
|
|
83
|
+
|
|
84
|
+
services[index][payload.property] = payload.value;
|
|
85
|
+
|
|
86
|
+
return state.set('services', services);
|
|
87
|
+
}
|
|
88
|
+
case APPLICATION_DEFINITION_SERVICE_EDIT_CANCEL: {
|
|
89
|
+
const services = cloneDeep(state.services);
|
|
90
|
+
const index = findIndex(services, { id: payload.rowData.id });
|
|
91
|
+
|
|
92
|
+
services[index] = cloneDeep(services[index].backup);
|
|
93
|
+
delete services[index].backup;
|
|
94
|
+
|
|
95
|
+
if (services[index].newEntry === true) {
|
|
96
|
+
services.splice(index, 1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return state.merge({
|
|
100
|
+
editMode: false,
|
|
101
|
+
services: services
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
case APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_OPEN: {
|
|
105
|
+
let parametersData = {};
|
|
106
|
+
|
|
107
|
+
if (payload && payload.rowData) {
|
|
108
|
+
parametersData.serviceDefinition = {
|
|
109
|
+
id: payload.rowData.id,
|
|
110
|
+
name: payload.rowData.name,
|
|
111
|
+
hostgroup_id: payload.rowData.hostgroup
|
|
112
|
+
}
|
|
113
|
+
parametersData.parameters = payload.rowData.parameters;
|
|
114
|
+
|
|
115
|
+
if (parametersData.parameters.length > 0) {
|
|
116
|
+
parametersData.mode = 'editDefinition';
|
|
117
|
+
} else {
|
|
118
|
+
parametersData.mode = 'newDefinition';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return state.merge({
|
|
123
|
+
parametersData: parametersData,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
case APPLICATION_DEFINITION_PARAMETER_SELECTION_MODAL_CLOSE: {
|
|
127
|
+
if (payload.mode == 'save') {
|
|
128
|
+
const services = cloneDeep(state.services);
|
|
129
|
+
const index = findIndex(services, { id: state.parametersData.serviceDefinition.id });
|
|
130
|
+
services[index].parameters = cloneDeep(payload.serviceParameterSelection);
|
|
131
|
+
|
|
132
|
+
return state.merge({
|
|
133
|
+
parametersData: null,
|
|
134
|
+
services: services,
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
return state.merge({
|
|
138
|
+
parametersData: null,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
return state;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export default applicationDefinitionConf;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const applicationDefinitionConf = state => state.foremanAcd.applicationDefinitionConf;
|
|
2
|
+
|
|
3
|
+
export const selectEditMode = state => applicationDefinitionConf(state).editMode;
|
|
4
|
+
export const selectServices = state => applicationDefinitionConf(state).services;
|
|
5
|
+
export const selectColumns = state => applicationDefinitionConf(state).columns;
|
|
6
|
+
export const selectParametersData = state => applicationDefinitionConf(state).parametersData;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { bindActionCreators } from 'redux';
|
|
2
|
+
import { connect } from 'react-redux';
|
|
3
|
+
|
|
4
|
+
import './ApplicationDefinition.scss';
|
|
5
|
+
import ApplicationDefinition from './ApplicationDefinition';
|
|
6
|
+
import * as ApplicationDefinitionActions from './ApplicationDefinitionActions';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
selectEditMode,
|
|
10
|
+
selectServices,
|
|
11
|
+
selectColumns,
|
|
12
|
+
selectParametersData,
|
|
13
|
+
} from './ApplicationDefinitionSelectors';
|
|
14
|
+
|
|
15
|
+
const mapStateToProps = state => ({
|
|
16
|
+
editMode: selectEditMode(state),
|
|
17
|
+
services: selectServices(state),
|
|
18
|
+
columns: selectColumns(state),
|
|
19
|
+
parametersData: selectParametersData(state),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const mapDispatchToProps = dispatch =>
|
|
23
|
+
bindActionCreators(ApplicationDefinitionActions, dispatch);
|
|
24
|
+
|
|
25
|
+
export default connect(
|
|
26
|
+
mapStateToProps,
|
|
27
|
+
mapDispatchToProps
|
|
28
|
+
)(ApplicationDefinition);
|
|
29
|
+
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import {
|
|
4
|
+
Icon,
|
|
5
|
+
Button,
|
|
6
|
+
} from 'patternfly-react';
|
|
7
|
+
import * as resolve from 'table-resolver';
|
|
8
|
+
import ForemanModal from 'foremanReact/components/ForemanModal';
|
|
9
|
+
import Select from 'foremanReact/components/common/forms/Select';
|
|
10
|
+
import ParameterSelection from '../ParameterSelection';
|
|
11
|
+
import AddTableEntry from '../common/AddTableEntry';
|
|
12
|
+
import DeleteTableEntry from '../common/DeleteTableEntry';
|
|
13
|
+
import RailsData from '../common/RailsData'
|
|
14
|
+
import AppDefinitionSelector from './components/AppDefinitionSelector';
|
|
15
|
+
import ServiceCounter from './components/ServiceCounter';
|
|
16
|
+
import { arrayToObject } from '../../helper';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
Table,
|
|
20
|
+
FormControl,
|
|
21
|
+
inlineEditFormatterFactory,
|
|
22
|
+
} from 'patternfly-react';
|
|
23
|
+
|
|
24
|
+
class ApplicationInstance extends React.Component {
|
|
25
|
+
|
|
26
|
+
constructor(props) {
|
|
27
|
+
super(props);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
isEditing({rowData}) {
|
|
31
|
+
return (rowData.backup !== undefined);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
validateParameters() {
|
|
35
|
+
let result = true;
|
|
36
|
+
let msg = "";
|
|
37
|
+
|
|
38
|
+
this.props.hosts.forEach(h => {
|
|
39
|
+
if (h.parameters.map(e => e.value).filter(i => i == "").length > 0) {
|
|
40
|
+
result = false;
|
|
41
|
+
|
|
42
|
+
if (msg == "") {
|
|
43
|
+
msg += "For some hosts the values for some parameters are missing. Check the values for these hosts:\n";
|
|
44
|
+
}
|
|
45
|
+
msg += "- "+ h.hostname +"\n";
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const invalidMinServices = this.props.services.filter(s => (Number(s.minCount) != 0) && (s.currentCount < s.minCount));
|
|
50
|
+
const invalidMaxServices = this.props.services.filter(s => (Number(s.maxCount) != 0) && (s.currentCount > s.maxCount));
|
|
51
|
+
|
|
52
|
+
if (invalidMinServices.length > 0 || invalidMaxServices.length > 0) {
|
|
53
|
+
result = false;
|
|
54
|
+
|
|
55
|
+
if (msg != "") {
|
|
56
|
+
msg += "\n";
|
|
57
|
+
}
|
|
58
|
+
msg += "Unachieved service counts: \n";
|
|
59
|
+
|
|
60
|
+
invalidMinServices.map(s => { msg += "- service "+ s.name +" expects at least "+ s.minCount +" configured hosts" });
|
|
61
|
+
invalidMaxServices.map(s => { msg += "- service "+ s.name +" expects no more than "+ s.maxCount +" configured hosts" });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (result === false) {
|
|
65
|
+
window.alert(msg);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
componentDidMount() {
|
|
71
|
+
const {
|
|
72
|
+
data: { mode, appDefinition, hosts, loadAppDefinitionUrl },
|
|
73
|
+
initApplicationInstance,
|
|
74
|
+
addApplicationInstanceHost,
|
|
75
|
+
deleteApplicationInstanceHost,
|
|
76
|
+
activateEditApplicationInstanceHost,
|
|
77
|
+
changeEditApplicationInstanceHost,
|
|
78
|
+
openParameterSelectionModal,
|
|
79
|
+
closeParameterSelectionModal,
|
|
80
|
+
loadApplicationDefinition,
|
|
81
|
+
} = this.props;
|
|
82
|
+
|
|
83
|
+
if (mode === 'editInstance') {
|
|
84
|
+
loadApplicationDefinition(appDefinition.id, { url: loadAppDefinitionUrl });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const inlineEditButtonsFormatter = inlineEditFormatterFactory({
|
|
88
|
+
isEditing: additionalData => this.props.editMode,
|
|
89
|
+
renderValue: (value, additionalData) => (
|
|
90
|
+
<td style={{ padding: '2px' }}>
|
|
91
|
+
<Button
|
|
92
|
+
bsStyle="default"
|
|
93
|
+
onClick={() => activateEditApplicationInstanceHost(additionalData)}
|
|
94
|
+
>
|
|
95
|
+
<Icon type="pf" name="edit" />
|
|
96
|
+
</Button>
|
|
97
|
+
<Button
|
|
98
|
+
bsStyle="default"
|
|
99
|
+
onClick={() => openParameterSelectionModal(additionalData)}
|
|
100
|
+
>
|
|
101
|
+
<Icon type="pf" name="settings" />
|
|
102
|
+
</Button>
|
|
103
|
+
<DeleteTableEntry
|
|
104
|
+
hidden={false}
|
|
105
|
+
disabled={false}
|
|
106
|
+
onDeleteTableEntry={deleteApplicationInstanceHost}
|
|
107
|
+
additionalData={additionalData}
|
|
108
|
+
/>
|
|
109
|
+
</td>
|
|
110
|
+
),
|
|
111
|
+
renderEdit: (value, additionalData) => (
|
|
112
|
+
<td style={{ padding: '2px' }}>
|
|
113
|
+
<Button bsStyle="default" disabled>
|
|
114
|
+
<Icon type="pf" name="edit" />
|
|
115
|
+
</Button>
|
|
116
|
+
<Button bsStyle="default" disabled>
|
|
117
|
+
<Icon type="pf" name="settings" />
|
|
118
|
+
</Button>
|
|
119
|
+
<DeleteTableEntry
|
|
120
|
+
hidden={false}
|
|
121
|
+
disabled={true}
|
|
122
|
+
onDeleteTableEntry={deleteApplicationInstanceHost}
|
|
123
|
+
additionalData={additionalData}
|
|
124
|
+
/>
|
|
125
|
+
</td>
|
|
126
|
+
)
|
|
127
|
+
});
|
|
128
|
+
this.inlineEditButtonsFormatter = inlineEditButtonsFormatter;
|
|
129
|
+
|
|
130
|
+
const headerFormatter = value => <Table.Heading>{value}</Table.Heading>;
|
|
131
|
+
this.headerFormatter = headerFormatter;
|
|
132
|
+
|
|
133
|
+
const inlineEditFormatterImpl = {
|
|
134
|
+
renderValue: (value, additionalData) => (
|
|
135
|
+
<td>
|
|
136
|
+
<span className="static">{value}</span>
|
|
137
|
+
</td>
|
|
138
|
+
),
|
|
139
|
+
renderEditText: (value, additionalData, subtype='text') => (
|
|
140
|
+
<td className="editing">
|
|
141
|
+
<FormControl
|
|
142
|
+
type={subtype}
|
|
143
|
+
defaultValue={value}
|
|
144
|
+
onBlur={e => changeEditApplicationInstanceHost(e.target.value, additionalData) }
|
|
145
|
+
/>
|
|
146
|
+
</td>
|
|
147
|
+
),
|
|
148
|
+
renderEditSelect: (value, additionalData, options) => (
|
|
149
|
+
<td className="editing">
|
|
150
|
+
<Select
|
|
151
|
+
value={value.toString()}
|
|
152
|
+
onChange={e => changeEditApplicationInstanceHost(e.target.value, additionalData) }
|
|
153
|
+
options={options}
|
|
154
|
+
allowClear
|
|
155
|
+
key="key"
|
|
156
|
+
/>
|
|
157
|
+
</td>
|
|
158
|
+
)
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const inlineEditFormatter = inlineEditFormatterFactory({
|
|
162
|
+
isEditing: additionalData => this.isEditing(additionalData),
|
|
163
|
+
renderValue: (value, additionalData) => {
|
|
164
|
+
let prettyValue = value;
|
|
165
|
+
if (additionalData.property == 'service') {
|
|
166
|
+
const serviceList = arrayToObject(this.props.services, "id", "name");
|
|
167
|
+
prettyValue = serviceList[value];
|
|
168
|
+
}
|
|
169
|
+
return inlineEditFormatterImpl.renderValue(prettyValue, additionalData)
|
|
170
|
+
},
|
|
171
|
+
renderEdit: (value, additionalData) => {
|
|
172
|
+
let prettyValue = value;
|
|
173
|
+
if (additionalData.property == 'service') {
|
|
174
|
+
const availableServices = this.props.services.filter(service => ((Number(service['maxCount']) == 0) || (service['currentCount'] < service['maxCount'])));
|
|
175
|
+
const serviceList = arrayToObject(availableServices, "id", "name");
|
|
176
|
+
|
|
177
|
+
if (additionalData.rowData.newEntry === true) {
|
|
178
|
+
return inlineEditFormatterImpl.renderEditSelect(value, additionalData, serviceList);
|
|
179
|
+
}
|
|
180
|
+
prettyValue = serviceList[value];
|
|
181
|
+
return inlineEditFormatterImpl.renderValue(prettyValue, additionalData)
|
|
182
|
+
}
|
|
183
|
+
return inlineEditFormatterImpl.renderEditText(prettyValue, additionalData);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
this.inlineEditFormatter = inlineEditFormatter;
|
|
187
|
+
|
|
188
|
+
initApplicationInstance(
|
|
189
|
+
appDefinition,
|
|
190
|
+
hosts,
|
|
191
|
+
this.headerFormatter,
|
|
192
|
+
this.inlineEditFormatter,
|
|
193
|
+
this.inlineEditButtonsFormatter,
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
render() {
|
|
198
|
+
const {
|
|
199
|
+
data: { mode, applications, organization, location, loadForemanDataUrl, loadAppDefinitionUrl },
|
|
200
|
+
appDefinition,
|
|
201
|
+
services,
|
|
202
|
+
hosts,
|
|
203
|
+
columns,
|
|
204
|
+
addApplicationInstanceHost,
|
|
205
|
+
confirmEditApplicationInstanceHost,
|
|
206
|
+
cancelEditApplicationInstanceHost,
|
|
207
|
+
openParameterSelectionModal,
|
|
208
|
+
closeParameterSelectionModal,
|
|
209
|
+
ParameterSelectionModal,
|
|
210
|
+
loadApplicationDefinition,
|
|
211
|
+
} = this.props;
|
|
212
|
+
|
|
213
|
+
// Start from validation when pressing submit. This should be in componentDidMount() but
|
|
214
|
+
// unfortunatley then the event wasn't fired. To make sure, that the on-click is only added
|
|
215
|
+
// once, there is a workaround to check if a css class "bound" exists.
|
|
216
|
+
$('input[type="submit"][name="commit"]:not(.bound)').addClass('bound').on('click', () => this.validateParameters());
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<span>
|
|
220
|
+
<div class="service-counter">
|
|
221
|
+
<ServiceCounter
|
|
222
|
+
title="Service counts"
|
|
223
|
+
serviceList={ services }
|
|
224
|
+
hostList={ hosts }
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
227
|
+
<div>
|
|
228
|
+
<AppDefinitionSelector
|
|
229
|
+
label="Application Definition"
|
|
230
|
+
editable={ mode == 'newInstance' }
|
|
231
|
+
viewText={ appDefinition.name }
|
|
232
|
+
options={ applications }
|
|
233
|
+
onChange={ loadApplicationDefinition }
|
|
234
|
+
selectValue={ appDefinition.id.toString() }
|
|
235
|
+
additionalData={{url: loadAppDefinitionUrl}}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
<div className="form-group">
|
|
239
|
+
<AddTableEntry
|
|
240
|
+
hidden={ false }
|
|
241
|
+
disabled={ this.props.editMode }
|
|
242
|
+
onAddTableEntry={ addApplicationInstanceHost }
|
|
243
|
+
/>
|
|
244
|
+
<Table.PfProvider
|
|
245
|
+
striped
|
|
246
|
+
bordered
|
|
247
|
+
hover
|
|
248
|
+
dataTable
|
|
249
|
+
inlineEdit
|
|
250
|
+
columns={columns}
|
|
251
|
+
components={{
|
|
252
|
+
body: {
|
|
253
|
+
row: Table.InlineEditRow,
|
|
254
|
+
cell: cellProps => cellProps.children
|
|
255
|
+
}
|
|
256
|
+
}}
|
|
257
|
+
>
|
|
258
|
+
<Table.Header headerRows={resolve.headerRows({ columns })} />
|
|
259
|
+
<Table.Body
|
|
260
|
+
rows={hosts}
|
|
261
|
+
rowKey="id"
|
|
262
|
+
onRow={(rowData, { rowIndex }) => ({
|
|
263
|
+
role: 'row',
|
|
264
|
+
isEditing: () => this.isEditing({ rowData }),
|
|
265
|
+
onCancel: () => cancelEditApplicationInstanceHost({ rowData, rowIndex }),
|
|
266
|
+
onConfirm: () => confirmEditApplicationInstanceHost({ rowData, rowIndex }),
|
|
267
|
+
last: rowIndex === services.length - 1
|
|
268
|
+
})}
|
|
269
|
+
/>
|
|
270
|
+
</Table.PfProvider>
|
|
271
|
+
<AddTableEntry
|
|
272
|
+
hidden={ false }
|
|
273
|
+
disabled={ this.props.editMode }
|
|
274
|
+
onAddTableEntry={ addApplicationInstanceHost }
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
<div>
|
|
278
|
+
<ForemanModal
|
|
279
|
+
id="AppInstanceParamSelection"
|
|
280
|
+
dialogClassName="param_selection_modal"
|
|
281
|
+
title="Parameter specification for Application Instance"
|
|
282
|
+
>
|
|
283
|
+
<ForemanModal.Header closeButton={false}>
|
|
284
|
+
Parameter specification
|
|
285
|
+
</ForemanModal.Header>
|
|
286
|
+
{this.props.parametersData ? (
|
|
287
|
+
<ParameterSelection
|
|
288
|
+
location={ location }
|
|
289
|
+
organization={ organization }
|
|
290
|
+
loadForemanDataUrl= { loadForemanDataUrl }
|
|
291
|
+
data={ this.props.parametersData }
|
|
292
|
+
/>
|
|
293
|
+
) : (<span>Empty</span>)
|
|
294
|
+
}
|
|
295
|
+
<ForemanModal.Footer>
|
|
296
|
+
<div>
|
|
297
|
+
<Button bsStyle="primary" onClick={() => closeParameterSelectionModal({ mode: 'save' })}>Save</Button>
|
|
298
|
+
<Button bsStyle="default" onClick={() => closeParameterSelectionModal({ mode: 'cancel' })}>Cancel</Button>
|
|
299
|
+
</div>
|
|
300
|
+
</ForemanModal.Footer>
|
|
301
|
+
</ForemanModal>
|
|
302
|
+
</div>
|
|
303
|
+
<RailsData
|
|
304
|
+
key='applications_instance'
|
|
305
|
+
view='app_instance'
|
|
306
|
+
parameter='hosts'
|
|
307
|
+
value={JSON.stringify(this.props.hosts)}
|
|
308
|
+
/>
|
|
309
|
+
</span>
|
|
310
|
+
)};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
ApplicationInstance.defaultProps = {
|
|
314
|
+
error: {},
|
|
315
|
+
appDefinition: { "id": '', "name": '' },
|
|
316
|
+
editMode: false,
|
|
317
|
+
services: [],
|
|
318
|
+
hosts: [],
|
|
319
|
+
parametersData: {},
|
|
320
|
+
columns: [],
|
|
321
|
+
editParamsOfRowId: null,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
ApplicationInstance.propTypes = {
|
|
325
|
+
initApplicationInstance: PropTypes.func,
|
|
326
|
+
editMode: PropTypes.bool.isRequired,
|
|
327
|
+
services: PropTypes.array,
|
|
328
|
+
appDefinition: PropTypes.object,
|
|
329
|
+
columns: PropTypes.array,
|
|
330
|
+
loadApplicationDefinition: PropTypes.func,
|
|
331
|
+
addApplicationInstanceHost: PropTypes.func,
|
|
332
|
+
deleteApplicationInstanceHost: PropTypes.func,
|
|
333
|
+
activateEditApplicationInstanceHost: PropTypes.func,
|
|
334
|
+
confirmEditApplicationInstanceHost: PropTypes.func,
|
|
335
|
+
cancelEditApplicationInstanceHost: PropTypes.func,
|
|
336
|
+
changeEditApplicationInstanceHost: PropTypes.func,
|
|
337
|
+
openParameterSelectionModal: PropTypes.func,
|
|
338
|
+
closeParameterSelectionModal: PropTypes.func,
|
|
339
|
+
parametersData: PropTypes.object,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export default ApplicationInstance;
|