foreman_acd 0.7.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/app/controllers/foreman_acd/ansible_playbooks_controller.rb +17 -2
  4. data/app/controllers/foreman_acd/app_definitions_controller.rb +104 -7
  5. data/app/controllers/foreman_acd/app_instances_controller.rb +15 -30
  6. data/app/controllers/foreman_acd/concerns/app_instance_mixins.rb +36 -0
  7. data/app/controllers/ui_acd_controller.rb +42 -3
  8. data/app/lib/actions/foreman_acd/run_configurator.rb +1 -0
  9. data/app/models/concerns/foreman_acd/host_managed_extensions.rb +40 -27
  10. data/app/models/foreman_acd/app_instance.rb +47 -2
  11. data/app/models/foreman_acd/foreman_host.rb +8 -0
  12. data/app/services/foreman_acd/app_deployer.rb +19 -2
  13. data/app/services/foreman_acd/inventory_creator.rb +11 -1
  14. data/app/views/foreman_acd/app_definitions/_form.html.erb +4 -0
  15. data/app/views/foreman_acd/app_definitions/import.html.erb +20 -1
  16. data/app/views/foreman_acd/app_definitions/index.html.erb +3 -6
  17. data/app/views/foreman_acd/app_instances/_form.html.erb +4 -0
  18. data/app/views/foreman_acd/app_instances/index.html.erb +15 -11
  19. data/app/views/foreman_acd/app_instances/report.html.erb +7 -2
  20. data/app/views/ui_acd/host_report.json.rabl +4 -0
  21. data/app/views/ui_acd/report_data.json.rabl +10 -0
  22. data/app/views/ui_acd/validate_hostname.json.rabl +6 -0
  23. data/config/routes.rb +3 -0
  24. data/db/migrate/20210818125913_add_is_existing_host_to_foreman_host.rb +8 -0
  25. data/db/migrate/20210902110645_add_initial_configure_task.rb +8 -0
  26. data/lib/foreman_acd/plugin.rb +9 -9
  27. data/lib/foreman_acd/version.rb +1 -1
  28. data/lib/foreman_acd.rb +27 -9
  29. data/package.json +8 -25
  30. data/test/controllers/ui_acd_controller_test.rb +4 -1
  31. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +2 -0
  32. data/webpack/__snapshots__/helper.test.js.snap +1 -1
  33. data/webpack/components/ApplicationDefinition/ApplicationDefinition.js +34 -10
  34. data/webpack/components/ApplicationDefinition/ApplicationDefinitionActions.js +12 -0
  35. data/webpack/components/ApplicationDefinition/ApplicationDefinitionConstants.js +1 -0
  36. data/webpack/components/ApplicationDefinition/ApplicationDefinitionReducer.js +30 -9
  37. data/webpack/components/ApplicationDefinition/ApplicationDefinitionSelectors.js +4 -0
  38. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinition.test.js +1 -0
  39. data/webpack/components/ApplicationDefinition/__tests__/ApplicationDefinitionSelectors.test.js +12 -0
  40. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinition.test.js.snap +31 -5
  41. data/webpack/components/ApplicationDefinition/__tests__/__snapshots__/ApplicationDefinitionSelectors.test.js.snap +8 -0
  42. data/webpack/components/ApplicationDefinition/components/AnsiblePlaybookSelector.js +1 -1
  43. data/webpack/components/ApplicationDefinition/components/__tests__/__snapshots__/AnsiblePlaybookSelector.test.js.snap +3 -3
  44. data/webpack/components/ApplicationDefinition/index.js +8 -0
  45. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.js +214 -0
  46. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImport.scss +1 -0
  47. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportActions.js +161 -0
  48. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportConstants.js +6 -0
  49. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportReducer.js +79 -0
  50. data/webpack/components/ApplicationDefinitionImport/ApplicationDefinitionImportSelectors.js +8 -0
  51. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportConfData_1.fixtures.js +129 -0
  52. data/webpack/components/ApplicationDefinitionImport/__fixtures__/applicationDefinitionImportReducer.fixtures.js +29 -0
  53. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImport.test.js +20 -0
  54. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportReducer.test.js +43 -0
  55. data/webpack/components/ApplicationDefinitionImport/__tests__/ApplicationDefinitionImportSelectors.test.js +29 -0
  56. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImport.test.js.snap +62 -0
  57. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportReducer.test.js.snap +362 -0
  58. data/webpack/components/ApplicationDefinitionImport/__tests__/__snapshots__/ApplicationDefinitionImportSelectors.test.js.snap +130 -0
  59. data/webpack/components/ApplicationDefinitionImport/index.js +32 -0
  60. data/webpack/components/ApplicationInstance/ApplicationInstance.js +102 -26
  61. data/webpack/components/ApplicationInstance/ApplicationInstanceActions.js +118 -6
  62. data/webpack/components/ApplicationInstance/ApplicationInstanceConstants.js +4 -0
  63. data/webpack/components/ApplicationInstance/ApplicationInstanceHelper.js +15 -0
  64. data/webpack/components/ApplicationInstance/ApplicationInstanceReducer.js +71 -30
  65. data/webpack/components/ApplicationInstance/ApplicationInstanceSelectors.js +4 -0
  66. data/webpack/components/ApplicationInstance/__fixtures__/applicationInstanceReducer.fixtures.js +2 -0
  67. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstance.test.js +1 -0
  68. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceReducer.test.js +12 -0
  69. data/webpack/components/ApplicationInstance/__tests__/ApplicationInstanceSelectors.test.js +12 -0
  70. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstance.test.js.snap +98 -7
  71. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceReducer.test.js.snap +271 -0
  72. data/webpack/components/ApplicationInstance/__tests__/__snapshots__/ApplicationInstanceSelectors.test.js.snap +8 -0
  73. data/webpack/components/ApplicationInstance/components/AppDefinitionSelector.js +1 -0
  74. data/webpack/components/ApplicationInstance/components/ServiceCounter.js +1 -1
  75. data/webpack/components/ApplicationInstance/helper.js +0 -0
  76. data/webpack/components/ApplicationInstance/index.js +8 -0
  77. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReport.js +81 -6
  78. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportActions.js +35 -1
  79. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportConstants.js +3 -0
  80. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportReducer.js +19 -0
  81. data/webpack/components/ApplicationInstanceReport/ApplicationInstanceReportSelectors.js +4 -0
  82. data/webpack/components/ApplicationInstanceReport/__tests__/__snapshots__/ApplicationInstanceReport.test.js.snap +1 -124
  83. data/webpack/components/ApplicationInstanceReport/index.js +8 -1
  84. data/webpack/components/ExistingHostSelection/ExistingHostSelection.js +104 -0
  85. data/webpack/components/ExistingHostSelection/ExistingHostSelection.scss +15 -0
  86. data/webpack/components/ExistingHostSelection/ExistingHostSelectionActions.js +71 -0
  87. data/webpack/components/ExistingHostSelection/ExistingHostSelectionConstants.js +4 -0
  88. data/webpack/components/ExistingHostSelection/ExistingHostSelectionHelper.js +0 -0
  89. data/webpack/components/ExistingHostSelection/ExistingHostSelectionReducer.js +90 -0
  90. data/webpack/components/ExistingHostSelection/ExistingHostSelectionSelectors.js +8 -0
  91. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionConfData_1.fixtures.js +191 -0
  92. data/webpack/components/ExistingHostSelection/__fixtures__/existingHostSelectionReducer.fixtures.js +203 -0
  93. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelection.test.js +19 -0
  94. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionReducer.test.js +59 -0
  95. data/webpack/components/ExistingHostSelection/__tests__/ExistingHostSelectionSelectors.test.js +36 -0
  96. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelection.test.js.snap +35 -0
  97. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionReducer.test.js.snap +614 -0
  98. data/webpack/components/ExistingHostSelection/__tests__/__snapshots__/ExistingHostSelectionSelectors.test.js.snap +27 -0
  99. data/webpack/components/ExistingHostSelection/components/ServiceSelector.js +48 -0
  100. data/webpack/components/ExistingHostSelection/components/__tests__/ServiceSelector.test.js +35 -0
  101. data/webpack/components/ExistingHostSelection/components/__tests__/__snapshots__/ServiceSelector.test.js.snap +77 -0
  102. data/webpack/components/ExistingHostSelection/index.js +26 -0
  103. data/webpack/components/ParameterSelection/ParameterSelection.js +103 -1
  104. data/webpack/components/ParameterSelection/ParameterSelection.scss +7 -0
  105. data/webpack/components/ParameterSelection/ParameterSelectionActions.js +46 -4
  106. data/webpack/components/ParameterSelection/ParameterSelectionConstants.js +2 -0
  107. data/webpack/components/ParameterSelection/ParameterSelectionHelper.js +5 -1
  108. data/webpack/components/ParameterSelection/ParameterSelectionReducer.js +52 -11
  109. data/webpack/components/ParameterSelection/ParameterSelectionSelectors.js +2 -0
  110. data/webpack/components/ParameterSelection/__fixtures__/parameterSelectionData_1.fixtures.js +8 -0
  111. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionReducer.test.js +2 -0
  112. data/webpack/components/ParameterSelection/__tests__/ParameterSelectionSelectors.test.js +6 -0
  113. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelection.test.js.snap +96 -0
  114. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionReducer.test.js.snap +117 -17
  115. data/webpack/components/ParameterSelection/__tests__/__snapshots__/ParameterSelectionSelectors.test.js.snap +13 -0
  116. data/webpack/components/ParameterSelection/index.js +4 -1
  117. data/webpack/components/SyncGitRepo/SyncGitRepo.js +2 -10
  118. data/webpack/components/SyncGitRepo/SyncGitRepoActions.js +2 -3
  119. data/webpack/components/SyncGitRepo/SyncGitRepoConstants.js +0 -1
  120. data/webpack/components/SyncGitRepo/__tests__/__snapshots__/SyncGitRepo.test.js.snap +1 -0
  121. data/webpack/components/SyncGitRepo/components/FormTextInput.js +1 -1
  122. data/webpack/components/SyncGitRepo/components/ScmTypeSelector.js +3 -2
  123. data/webpack/components/common/DeleteTableEntry.js +16 -2
  124. data/webpack/components/common/__tests__/__snapshots__/DeleteTableEntry.test.js.snap +38 -0
  125. data/webpack/helper.js +21 -1
  126. data/webpack/helper.test.js +20 -1
  127. data/webpack/index.js +5 -0
  128. data/webpack/js-yaml.js +3874 -0
  129. data/webpack/reducer.js +13 -2
  130. data/webpack/test_setup.js +0 -2
  131. metadata +46 -2
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import {
4
4
  Icon,
5
5
  Button,
6
+ MessageDialog,
6
7
  } from 'patternfly-react';
7
8
  import * as resolve from 'table-resolver';
8
9
  import ForemanModal from 'foremanReact/components/ForemanModal';
@@ -12,6 +13,7 @@ import {
12
13
  } from 'foremanReact/common/I18n';
13
14
  import Select from 'foremanReact/components/common/forms/Select';
14
15
  import ParameterSelection from '../ParameterSelection';
16
+ import ExistingHostSelection from '../ExistingHostSelection';
15
17
  import AddTableEntry from '../common/AddTableEntry';
16
18
  import DeleteTableEntry from '../common/DeleteTableEntry';
17
19
  import RailsData from '../common/RailsData';
@@ -41,7 +43,7 @@ class ApplicationInstance extends React.Component {
41
43
  return (rowData.backup !== undefined);
42
44
  }
43
45
 
44
- addTableEntryAllowed() {
46
+ changeDataAllowed() {
45
47
  return this.props.editMode || this.props.appDefinition.id == ''
46
48
  }
47
49
 
@@ -90,7 +92,7 @@ class ApplicationInstance extends React.Component {
90
92
 
91
93
  componentDidMount() {
92
94
  const {
93
- data: { mode, appDefinition, hosts, ansibleVarsAll, appDefinitionUrl },
95
+ data: { mode, appDefinition, hosts, ansibleVarsAll, appDefinitionUrl, supportedPlugins },
94
96
  initApplicationInstance,
95
97
  addApplicationInstanceHost,
96
98
  deleteApplicationInstanceHost,
@@ -105,29 +107,36 @@ class ApplicationInstance extends React.Component {
105
107
  loadApplicationDefinition(appDefinition.id, { url: appDefinitionUrl });
106
108
  }
107
109
 
110
+ const already_deployed_msg = __("This is an already deployed host. Changing the parameters is not possible!");
111
+
108
112
  const inlineEditButtonsFormatter = inlineEditFormatterFactory({
109
113
  isEditing: additionalData => this.props.editMode,
110
114
  renderValue: (value, additionalData) => (
111
115
  <td style={{ padding: '2px' }}>
116
+ { additionalData.rowData.isExistingHost == true ? (
117
+ <Icon style={{marginRight: 8, marginLeft: 2}} type="pf" name="info" title={already_deployed_msg} />
118
+ ) : (<span></span>)}
112
119
  <Button
113
120
  bsStyle="default"
114
121
  onClick={() => activateEditApplicationInstanceHost(additionalData)}
115
122
  >
116
- <Icon type="pf" name="edit" title={__("edit entry")} />
123
+ <Icon type="pf" name="edit" title={__("Edit entry")} />
117
124
  </Button>
118
125
  &nbsp;
119
- <Button
120
- bsStyle="default"
121
- onClick={() => openForemanParameterSelectionModal(additionalData)}
122
- >
123
- <Icon type="pf" name="settings" title={__("change parameters")} />
124
- </Button>
126
+ { additionalData.rowData.isExistingHost == false ? (
127
+ <Button
128
+ bsStyle="default"
129
+ onClick={() => openForemanParameterSelectionModal(additionalData)}
130
+ >
131
+ <Icon type="pf" name="settings" title={__("Change parameters")} />
132
+ </Button>
133
+ ) : (<span></span>)}
125
134
  &nbsp;
126
135
  <Button
127
136
  bsStyle="default"
128
137
  onClick={() => openAnsibleParameterSelectionModal(additionalData)}
129
138
  >
130
- <span title={__("change ansible variables")}>A</span>
139
+ <span title={__("Change ansible variables")}>A</span>
131
140
  </Button>
132
141
  &nbsp;
133
142
  <DeleteTableEntry
@@ -140,13 +149,18 @@ class ApplicationInstance extends React.Component {
140
149
  ),
141
150
  renderEdit: (value, additionalData) => (
142
151
  <td style={{ padding: '2px' }}>
152
+ { additionalData.rowData.isExistingHost == true ? (
153
+ <Icon style={{marginRight: 8, marginLeft: 2}} type="pf" name="info" title={already_deployed_msg} />
154
+ ) : (<span></span>)}
143
155
  <Button bsStyle="default" disabled>
144
156
  <Icon type="pf" name={__("edit")} />
145
157
  </Button>
146
158
  &nbsp;
147
- <Button bsStyle="default" disabled>
148
- <Icon type="pf" name={__("settings")} />
149
- </Button>
159
+ { additionalData.rowData.isExistingHost == false ? (
160
+ <Button bsStyle="default" disabled>
161
+ <Icon type="pf" name={__("settings")} />
162
+ </Button>
163
+ ) : (<span></span>)}
150
164
  &nbsp;
151
165
  <Button bsStyle="default" disabled>
152
166
  <span>A</span>
@@ -230,6 +244,7 @@ class ApplicationInstance extends React.Component {
230
244
  appDefinition,
231
245
  hosts,
232
246
  ansibleVarsAll,
247
+ supportedPlugins,
233
248
  this.headerFormatter,
234
249
  this.inlineEditFormatter,
235
250
  this.inlineEditButtonsFormatter,
@@ -239,16 +254,20 @@ class ApplicationInstance extends React.Component {
239
254
  render() {
240
255
  const {
241
256
  data: { mode, applications, organization, location, foremanDataUrl, appDefinitionUrl },
257
+ showAlertModal, alertModalText, alertModalTitle, closeAlertModal,
242
258
  appDefinition,
243
259
  services,
244
260
  hosts,
245
261
  columns,
262
+ hiddenForemanParameterTypes,
246
263
  addApplicationInstanceHost,
247
264
  confirmEditApplicationInstanceHost,
248
265
  cancelEditApplicationInstanceHost,
249
266
  closeForemanParameterSelectionModal,
250
267
  openAnsibleParameterSelectionModal,
251
268
  closeAnsibleParameterSelectionModal,
269
+ openAddExistingHostsModal,
270
+ closeAddExistingHostsModal,
252
271
  changeParameterSelectionMode,
253
272
  loadApplicationDefinition,
254
273
  } = this.props;
@@ -263,7 +282,17 @@ class ApplicationInstance extends React.Component {
263
282
 
264
283
  return (
265
284
  <span>
266
- <div class="service-counter">
285
+ <MessageDialog
286
+ show={showAlertModal}
287
+ onHide={closeAlertModal}
288
+ primaryAction={closeAlertModal}
289
+ primaryActionButtonContent={__('OK')}
290
+ primaryActionButtonBsStyle={"danger"}
291
+ icon={<Icon type="pf" name="error-circle-o" />}
292
+ title={alertModalTitle}
293
+ primaryContent={alertModalText}
294
+ />
295
+ <div className="service-counter">
267
296
  <ServiceCounter
268
297
  title="Service counts"
269
298
  serviceList={ services }
@@ -273,6 +302,7 @@ class ApplicationInstance extends React.Component {
273
302
  <div>
274
303
  <AppDefinitionSelector
275
304
  label="Application Definition"
305
+ hidden={ false }
276
306
  editable={ mode == 'newInstance' }
277
307
  viewText={ appDefinition.name }
278
308
  options={ applications }
@@ -281,9 +311,9 @@ class ApplicationInstance extends React.Component {
281
311
  additionalData={{url: appDefinitionUrl}}
282
312
  />
283
313
  {appDefinition.id == '' ? (
284
- <p style={{ paddingTop: 25 }}>
314
+ <div style={{ paddingTop: 25 }}>
285
315
  <pre>{ "App Definition can't be blank" }</pre>
286
- </p>
316
+ </div>
287
317
  ) : (<div></div>)}
288
318
  </div>
289
319
  <div className="form-group">
@@ -309,27 +339,38 @@ class ApplicationInstance extends React.Component {
309
339
  role: 'row',
310
340
  isEditing: () => this.isEditing({ rowData }),
311
341
  onCancel: () => cancelEditApplicationInstanceHost({ rowData, rowIndex }),
312
- onConfirm: () => confirmEditApplicationInstanceHost({ rowData, rowIndex }),
342
+ onConfirm: () => confirmEditApplicationInstanceHost({ rowData, rowIndex, appDefinition }),
313
343
  last: rowIndex === services.length - 1
314
344
  })}
315
345
  />
316
346
  </Table.PfProvider>
317
347
  <AddTableEntry
318
348
  hidden={ false }
319
- disabled={ this.addTableEntryAllowed() }
349
+ disabled={ this.changeDataAllowed() }
320
350
  onAddTableEntry={ addApplicationInstanceHost }
321
351
  />
352
+ <span style={{ marginLeft: 10 }}>
353
+ <Button
354
+ bsStyle="default"
355
+ disabled={ this.changeDataAllowed() }
356
+ onClick={() => openAddExistingHostsModal({
357
+ isAllGroup: true
358
+ })}
359
+ >
360
+ <Icon title={__("Add existing hosts")} type="pf" name="server" />
361
+ </Button>
362
+ </span>
322
363
  <span style={{ marginLeft: 30 }}>
323
364
  Ansible group vars 'all':
324
365
  <Button
325
366
  style={{ marginLeft: 10 }}
326
367
  bsStyle="default"
327
- disabled={ this.props.editMode }
368
+ disabled={ this.changeDataAllowed() }
328
369
  onClick={() => openAnsibleParameterSelectionModal({
329
370
  isAllGroup: true
330
371
  })}
331
372
  >
332
- <span title={__("change ansible variables for 'all'")}>A</span>
373
+ <span title={__("Change ansible variables for 'all'")}>A</span>
333
374
  </Button>
334
375
  </span>
335
376
  </div>
@@ -340,12 +381,13 @@ class ApplicationInstance extends React.Component {
340
381
  title={__("Foreman Parameter specification for Application Instance")}
341
382
  >
342
383
  <ForemanModal.Header closeButton={false}>
343
- Parameter specification
384
+ {__("Parameter specification")}
344
385
  </ForemanModal.Header>
345
386
  {this.props.parametersData ? (
346
387
  <ParameterSelection
347
388
  editModeCallback={ (hide) => changeParameterSelectionMode({ mode: hide })}
348
389
  paramType={ PARAMETER_SELECTION_PARAM_TYPE_FOREMAN }
390
+ hiddenParameterTypes={ hiddenForemanParameterTypes }
349
391
  location={ location }
350
392
  organization={ organization }
351
393
  paramDataUrl= { foremanDataUrl }
@@ -368,7 +410,7 @@ class ApplicationInstance extends React.Component {
368
410
  title={__("Ansible group variables for Application Instance")}
369
411
  >
370
412
  <ForemanModal.Header closeButton={false}>
371
- Parameter specification
413
+ {__("Parameter specification")}
372
414
  </ForemanModal.Header>
373
415
  {this.props.parametersData ? (
374
416
  <ParameterSelection
@@ -388,19 +430,42 @@ class ApplicationInstance extends React.Component {
388
430
  </ForemanModal.Footer>
389
431
  </ForemanModal>
390
432
  </div>
433
+ <div>
434
+ <ForemanModal
435
+ id="AppInstanceAddExistingHosts"
436
+ dialogClassName="add_existing_hosts_modal"
437
+ title={__("Add existing hosts to an Application Instance")}
438
+ >
439
+ <ForemanModal.Header closeButton={false}>
440
+ {__("Existing hosts selection")}
441
+ </ForemanModal.Header>
442
+ <ExistingHostSelection
443
+ location={ location }
444
+ organization={ organization }
445
+ services={ services }
446
+ allHosts={ this.props.hosts }
447
+ />
448
+ <ForemanModal.Footer>
449
+ <div>
450
+ <Button bsStyle="primary" disabled={this.props.paramEditMode} onClick={() => closeAddExistingHostsModal({ mode: 'save' })}>{__("Save")}</Button>
451
+ <Button bsStyle="default" disabled={this.props.paramEditMode} onClick={() => closeAddExistingHostsModal({ mode: 'cancel' })}>{__("Cancel")}</Button>
452
+ </div>
453
+ </ForemanModal.Footer>
454
+ </ForemanModal>
455
+ </div>
391
456
  {validateResult == false ? (
392
- <p style={{ paddingTop: 25 }}>
457
+ <div style={{ paddingTop: 25 }}>
393
458
  <pre>{ validateMsg }</pre>
394
- </p>
459
+ </div>
395
460
  ) : (<div></div>)}
396
461
  <RailsData
397
- key='applications_instance'
462
+ key='application_instance_hosts_data'
398
463
  view='app_instance'
399
464
  parameter='hosts'
400
465
  value={JSON.stringify(this.props.hosts)}
401
466
  />
402
467
  <RailsData
403
- key='applications_instance'
468
+ key='application_instance_ansible_data'
404
469
  view='app_instance'
405
470
  parameter='ansible_vars_all'
406
471
  value={JSON.stringify(this.props.ansibleVarsAll)}
@@ -411,6 +476,9 @@ class ApplicationInstance extends React.Component {
411
476
 
412
477
  ApplicationInstance.defaultProps = {
413
478
  error: {},
479
+ showAlertModal: false,
480
+ alertModalText: '',
481
+ alertModalTitle: '',
414
482
  appDefinition: { "id": '', "name": '' },
415
483
  editMode: false,
416
484
  services: [],
@@ -418,18 +486,24 @@ ApplicationInstance.defaultProps = {
418
486
  ansibleVarsAll: [],
419
487
  parametersData: {},
420
488
  columns: [],
489
+ hiddenForemanParameterTypes: [],
421
490
  editParamsOfRowId: null,
422
491
  paramEditMode: false,
423
492
  }
424
493
 
425
494
  ApplicationInstance.propTypes = {
426
495
  initApplicationInstance: PropTypes.func,
496
+ showAlertModal: PropTypes.bool,
497
+ alertModalText: PropTypes.string,
498
+ alertModalTitle: PropTypes.string,
427
499
  editMode: PropTypes.bool.isRequired,
428
500
  services: PropTypes.array,
429
501
  appDefinition: PropTypes.object,
430
502
  columns: PropTypes.array,
503
+ hiddenForemanParameterTypes: PropTypes.array,
431
504
  hosts: PropTypes.array,
432
505
  ansibleVarsAll: PropTypes.array,
506
+ closeAlertModal: PropTypes.func,
433
507
  loadApplicationDefinition: PropTypes.func,
434
508
  addApplicationInstanceHost: PropTypes.func,
435
509
  deleteApplicationInstanceHost: PropTypes.func,
@@ -441,6 +515,8 @@ ApplicationInstance.propTypes = {
441
515
  closeForemanParameterSelectionModal: PropTypes.func,
442
516
  openAnsibleParameterSelectionModal: PropTypes.func,
443
517
  closeAnsibleParameterSelectionModal: PropTypes.func,
518
+ openAddExistingHostsModal: PropTypes.func,
519
+ closeAddExistingHostsModal: PropTypes.func,
444
520
  changeParameterSelectionMode: PropTypes.func,
445
521
  parametersData: PropTypes.object,
446
522
  paramEditMode: PropTypes.bool,
@@ -16,26 +16,35 @@ import {
16
16
 
17
17
  import {
18
18
  APPLICATION_INSTANCE_INIT,
19
+ APPLICATION_INSTANCE_CLOSE_ALERT_MODAL,
19
20
  APPLICATION_INSTANCE_HOST_DELETE,
20
21
  APPLICATION_INSTANCE_HOST_ADD,
21
22
  APPLICATION_INSTANCE_HOST_EDIT_ACTIVATE,
22
23
  APPLICATION_INSTANCE_HOST_EDIT_CONFIRM,
23
24
  APPLICATION_INSTANCE_HOST_EDIT_CHANGE,
24
25
  APPLICATION_INSTANCE_HOST_EDIT_CANCEL,
26
+ APPLICATION_INSTANCE_HOST_EDIT_ERROR,
25
27
  APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_OPEN,
26
28
  APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_CLOSE,
27
29
  APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_OPEN,
28
30
  APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_CLOSE,
31
+ APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_OPEN,
32
+ APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_CLOSE,
29
33
  APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_REQUEST,
30
34
  APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_SUCCESS,
31
35
  APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_FAILURE,
32
36
  APPLICATION_INSTANCE_CHANGE_PARAMETER_SELECTION_MODE,
33
37
  } from './ApplicationInstanceConstants';
34
38
 
39
+ import {
40
+ supportedPluginsToHiddenParameterTypes,
41
+ } from '../../helper';
42
+
35
43
  export const initApplicationInstance = (
36
44
  appDefinition,
37
45
  hosts,
38
46
  ansibleVarsAll,
47
+ supportedPlugins,
39
48
  headerFormatter,
40
49
  inlineEditFormatter,
41
50
  inlineEditButtonsFormatter,
@@ -112,6 +121,7 @@ export const initApplicationInstance = (
112
121
  initialState.appDefinition = appDefinition;
113
122
  initialState.hosts = hosts;
114
123
  initialState.ansibleVarsAll = ansibleVarsAll;
124
+ initialState.hiddenForemanParameterTypes = supportedPluginsToHiddenParameterTypes(supportedPlugins);
115
125
 
116
126
  dispatch({
117
127
  type: APPLICATION_INSTANCE_INIT,
@@ -127,6 +137,11 @@ const errorHandler = (msg, err) => {
127
137
  return { type: msg, payload: { error } };
128
138
  };
129
139
 
140
+ export const closeAlertModal = () => ({
141
+ type: APPLICATION_INSTANCE_CLOSE_ALERT_MODAL,
142
+ payload: {}
143
+ });
144
+
130
145
  export const loadApplicationDefinition = (
131
146
  applicationDefinitionId,
132
147
  additionalData,
@@ -167,12 +182,84 @@ export const activateEditApplicationInstanceHost = (additionalData) => ({
167
182
  },
168
183
  });
169
184
 
170
- export const confirmEditApplicationInstanceHost = (rowData) => ({
171
- type: APPLICATION_INSTANCE_HOST_EDIT_CONFIRM,
172
- payload: {
173
- ...rowData,
174
- },
175
- });
185
+ export const confirmEditApplicationInstanceHost = (
186
+ allData
187
+ ) => async(dispatch) => {
188
+
189
+ // Host name can not be empty
190
+
191
+ if (allData.rowData.hostname == '') {
192
+ dispatch({
193
+ type: APPLICATION_INSTANCE_HOST_EDIT_ERROR,
194
+ payload: __("Every host needs to have a valid name"),
195
+ });
196
+ return;
197
+ }
198
+
199
+ // Host name can only have specific characters
200
+
201
+ const hostname = allData.rowData.hostname.toLowerCase();
202
+ const hostnameRegex = /^[0-9a-z]([0-9a-z\-]{0,61}[0-9a-z])$/;
203
+
204
+ if (hostname.match(hostnameRegex) == undefined) {
205
+ dispatch({
206
+ type: APPLICATION_INSTANCE_HOST_EDIT_ERROR,
207
+ payload: __("The hostname uses not allowed characters. See https://en.wikipedia.org/wiki/Hostname#Syntax for more details."),
208
+ });
209
+ return;
210
+ }
211
+
212
+ // Service can not be empty
213
+
214
+ if (allData.rowData.service == '') {
215
+ dispatch({
216
+ type: APPLICATION_INSTANCE_HOST_EDIT_ERROR,
217
+ payload: __("Every host needs to be assigned to a service."),
218
+ });
219
+ return;
220
+ }
221
+
222
+ // Validation if host name is already used (only for new host entrys)
223
+
224
+ const url = '/acd/ui_acd_validate_hostname'
225
+ const validationData = {};
226
+
227
+ validationData['appDefId'] = allData.appDefinition.id;
228
+ validationData['serviceId'] = allData.rowData.service;
229
+ validationData['hostname'] = allData.rowData.hostname;
230
+
231
+ if (allData.rowData.newEntry === true) {
232
+ try {
233
+ const response = await api.get(url, {}, validationData);
234
+
235
+ if (response.data.result === true) {
236
+ dispatch({
237
+ type: APPLICATION_INSTANCE_HOST_EDIT_CONFIRM,
238
+ payload: {
239
+ ...allData,
240
+ }
241
+ });
242
+ } else {
243
+ dispatch({
244
+ type: APPLICATION_INSTANCE_HOST_EDIT_ERROR,
245
+ payload: __('Hostname \''+ allData.rowData.hostname +'\' is already used. This check also includes hosts outside this application instance.'),
246
+ });
247
+ }
248
+ } catch (error) {
249
+ dispatch({
250
+ type: APPLICATION_INSTANCE_HOST_EDIT_ERROR,
251
+ payload: __('Error during validation if hostname is already used.'),
252
+ });
253
+ }
254
+ } else {
255
+ dispatch({
256
+ type: APPLICATION_INSTANCE_HOST_EDIT_CONFIRM,
257
+ payload: {
258
+ ...allData,
259
+ }
260
+ });
261
+ }
262
+ };
176
263
 
177
264
  export const cancelEditApplicationInstanceHost = (rowData) => ({
178
265
  type: APPLICATION_INSTANCE_HOST_EDIT_CANCEL,
@@ -239,6 +326,31 @@ export const closeAnsibleParameterSelectionModal = (additionalData) => dispatch
239
326
  );
240
327
  }
241
328
 
329
+ export const openAddExistingHostsModal = (additionalData) => dispatch => {
330
+ dispatch({
331
+ type: APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_OPEN,
332
+ payload: {
333
+ ...additionalData,
334
+ }
335
+ });
336
+ dispatch(
337
+ setModalOpen({ id: 'AppInstanceAddExistingHosts' })
338
+ );
339
+ }
340
+
341
+ export const closeAddExistingHostsModal = (additionalData) => dispatch => {
342
+ dispatch({
343
+ type: APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_CLOSE,
344
+ payload: {
345
+ ...additionalData,
346
+ }
347
+ });
348
+
349
+ dispatch(
350
+ setModalClosed({ id: 'AppInstanceAddExistingHosts' })
351
+ );
352
+ }
353
+
242
354
  export const changeParameterSelectionMode = (additionalData) => ({
243
355
  type: APPLICATION_INSTANCE_CHANGE_PARAMETER_SELECTION_MODE,
244
356
  payload: {
@@ -1,4 +1,5 @@
1
1
  export const APPLICATION_INSTANCE_INIT = 'APPLICATION_INSTANCE_INIT';
2
+ export const APPLICATION_INSTANCE_CLOSE_ALERT_MODAL = 'APPLICATION_INSTANCE_CLOSE_ALERT_MODAL';
2
3
  export const APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_REQUEST = 'APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_REQUEST';
3
4
  export const APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_SUCCESS = 'APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_SUCCESS';
4
5
  export const APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_FAILURE = 'APPLICATION_INSTANCE_LOAD_APPLICATION_DEFINITION_FAILURE';
@@ -8,8 +9,11 @@ export const APPLICATION_INSTANCE_HOST_EDIT_ACTIVATE = 'APPLICATION_INSTANCE_HOS
8
9
  export const APPLICATION_INSTANCE_HOST_EDIT_CONFIRM = 'APPLICATION_INSTANCE_HOST_EDIT_CONFIRM';
9
10
  export const APPLICATION_INSTANCE_HOST_EDIT_CHANGE = 'APPLICATION_INSTANCE_HOST_EDIT_CHANGE';
10
11
  export const APPLICATION_INSTANCE_HOST_EDIT_CANCEL = 'APPLICATION_INSTANCE_HOST_EDIT_CANCEL';
12
+ export const APPLICATION_INSTANCE_HOST_EDIT_ERROR = 'APPLICATION_INSTANCE_HOST_EDIT_ERROR';
11
13
  export const APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_OPEN = 'APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_OPEN';
12
14
  export const APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_CLOSE = 'APPLICATION_INSTANCE_FOREMAN_PARAMETER_SELECTION_MODAL_CLOSE';
13
15
  export const APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_OPEN = 'APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_OPEN';
14
16
  export const APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_CLOSE = 'APPLICATION_INSTANCE_ANSIBLE_PARAMETER_SELECTION_MODAL_CLOSE';
17
+ export const APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_OPEN = 'APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_OPEN';
18
+ export const APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_CLOSE = 'APPLICATION_INSTANCE_ADD_EXISTING_HOSTS_MODAL_CLOSE';
15
19
  export const APPLICATION_INSTANCE_CHANGE_PARAMETER_SELECTION_MODE = 'APPLICATION_INSTANCE_CHANGE_PARAMETER_SELECTION_MODE';
@@ -0,0 +1,15 @@
1
+
2
+ function calculateServiceUsage(hostServiceId, services) {
3
+ const service = services.find(serv => serv['id'] == hostServiceId);
4
+ if ('currentCount' in service) {
5
+ service['currentCount'] += 1;
6
+ } else {
7
+ service['currentCount'] = 1;
8
+ }
9
+
10
+ return services;
11
+ }
12
+
13
+ export {
14
+ calculateServiceUsage,
15
+ };