foreman_snapshot_management 2.0.1 → 2.0.3

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/api/v2/snapshots_controller.rb +4 -1
  4. data/app/controllers/concerns/foreman/controller/parameters/snapshot.rb +1 -1
  5. data/app/controllers/foreman_snapshot_management/snapshots_controller.rb +16 -2
  6. data/app/models/foreman_snapshot_management/proxmox_extensions.rb +3 -3
  7. data/app/models/foreman_snapshot_management/snapshot.rb +4 -4
  8. data/app/models/foreman_snapshot_management/vmware_extensions.rb +13 -3
  9. data/app/views/foreman_snapshot_management/snapshots/_index.html.erb +2 -1
  10. data/app/views/foreman_snapshot_management/snapshots/select_multiple_host.html.erb +5 -3
  11. data/lib/foreman_snapshot_management/version.rb +1 -1
  12. data/locale/Makefile +7 -3
  13. data/locale/cs_CZ/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  14. data/locale/cs_CZ/foreman_snapshot_management.po +195 -0
  15. data/locale/de/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  16. data/locale/de/foreman_snapshot_management.po +10 -10
  17. data/locale/el/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  18. data/locale/el/foreman_snapshot_management.po +190 -0
  19. data/locale/en/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  20. data/locale/en/foreman_snapshot_management.po +22 -13
  21. data/locale/foreman_snapshot_management.pot +56 -35
  22. data/locale/fr/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  23. data/locale/fr/foreman_snapshot_management.po +196 -0
  24. data/locale/ja/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  25. data/locale/ja/foreman_snapshot_management.po +189 -0
  26. data/locale/ka/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  27. data/locale/ka/foreman_snapshot_management.po +190 -0
  28. data/locale/zh_CN/LC_MESSAGES/foreman_snapshot_management.mo +0 -0
  29. data/locale/zh_CN/foreman_snapshot_management.po +191 -0
  30. data/package.json +3 -2
  31. data/test/controllers/api/v2/snapshots_test.rb +113 -89
  32. data/test/controllers/foreman_snapshot_management/snapshots_controller_test.rb +72 -41
  33. data/webpack/components/SnapshotManagement/SnapshotManagement.js +2 -0
  34. data/webpack/components/SnapshotManagement/SnapshotManagementActions.js +1 -1
  35. data/webpack/components/SnapshotManagement/__tests__/__snapshots__/SnapshotManagementActions.test.js.snap +12 -12
  36. data/webpack/components/SnapshotManagement/components/SnapshotForm/SnapshotForm.js +60 -17
  37. data/webpack/components/SnapshotManagement/components/SnapshotForm/__tests__/__snapshots__/SnapshotForm.test.js.snap +262 -24
  38. data/webpack/components/SnapshotManagementCard/SnapshotManagementCard.js +61 -0
  39. data/webpack/components/SnapshotManagementCard/__tests__/SnapshotManagementCard.test.js +53 -0
  40. data/webpack/components/SnapshotManagementCard/__tests__/__snapshots__/SnapshotManagementCard.test.js.snap +132 -0
  41. data/webpack/components/SnapshotManagementCard/index.js +1 -0
  42. data/webpack/global_index.js +13 -0
  43. metadata +21 -5
@@ -1,13 +1,11 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import * as Yup from 'yup';
4
4
 
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
- // import CheckBox from 'foremanReact/components/common/forms/Checkbox';
7
- // import TextInput from 'foremanReact/components/common/forms/TextInput';
8
6
  import ForemanForm from 'foremanReact/components/common/forms/ForemanForm';
9
7
  import TextField from 'foremanReact/components/common/forms/TextField';
10
-
8
+ import { FieldLevelHelp } from 'patternfly-react';
11
9
  import { SNAPSHOT_CREATE_URL } from './SnapshotFormConstants';
12
10
  import './snapshotForm.scss';
13
11
 
@@ -18,6 +16,7 @@ const SnapshotForm = ({
18
16
  capabilities,
19
17
  ...props
20
18
  }) => {
19
+ const { setModalClosed } = props;
21
20
  let nameValidation = Yup.string().max(80, 'Too Long!');
22
21
  if (capabilities.limitSnapshotNameFormat)
23
22
  nameValidation = nameValidation
@@ -34,33 +33,53 @@ const SnapshotForm = ({
34
33
  name: nameValidation.required('is required'),
35
34
  description: Yup.string(),
36
35
  includeRam: Yup.bool(),
36
+ useQuiesce: Yup.bool(),
37
+ snapshotMode: Yup.string(),
37
38
  });
38
39
 
39
- const handleSubmit = async (values, actions) => {
40
+ const [snapshotMode, setSnapshotMode] = useState();
41
+
42
+ const options = [
43
+ { name: '', value: '' },
44
+ { name: 'Memory', value: __('Memory') },
45
+ ];
46
+ if (capabilities.quiesceOption) {
47
+ options.push({ name: 'Quiesce', value: __('Quiesce') });
48
+ }
49
+
50
+ const handleSubmit = (values, actions) => {
51
+ if (snapshotMode === 'Quiesce') {
52
+ values.useQuiesce = true;
53
+ values.includeRam = false;
54
+ } else if (snapshotMode === 'Memory') {
55
+ values.includeRam = true;
56
+ values.useQuiesce = false;
57
+ }
58
+
40
59
  const submitValues = {
41
60
  include_ram: values.includeRam || false,
61
+ quiesce: values.useQuiesce || false,
42
62
  snapshot: {
43
63
  name: values.name,
44
64
  description: values.description || '',
45
65
  },
46
66
  };
47
-
48
- await submitForm({
67
+ submitForm({
49
68
  item: 'Snapshot',
50
69
  url: SNAPSHOT_CREATE_URL.replace(':hostId', hostId),
51
70
  values: submitValues,
52
71
  message: __('Snapshot successfully created!'),
72
+ successCallback: setModalClosed,
73
+ actions,
53
74
  });
54
- actions.setSubmitting(false);
55
- props.setModalClosed();
56
75
  };
57
76
 
58
77
  return (
59
78
  <ForemanForm
60
- onSubmit={(values, actions) => handleSubmit(values, actions)}
79
+ onSubmit={handleSubmit}
61
80
  initialValues={initialValues}
62
81
  validationSchema={validationSchema}
63
- onCancel={() => props.setModalClosed()}
82
+ onCancel={setModalClosed}
64
83
  >
65
84
  <TextField
66
85
  name="name"
@@ -75,12 +94,31 @@ const SnapshotForm = ({
75
94
  label={__('Description')}
76
95
  inputClassName="col-md-8"
77
96
  />
78
- <TextField
79
- type="checkbox"
80
- name="includeRam"
81
- label={__('Include RAM')}
82
- inputClassName="col-md-8"
83
- />
97
+ <div className="form-group">
98
+ <label className="col-md-2 control-label">
99
+ {__('Snapshot Mode')}
100
+ <FieldLevelHelp
101
+ buttonClass="field-help"
102
+ placement="top"
103
+ content={__(
104
+ "Select Snapshot Mode between mutually exclusive options, 'Memory' (includes RAM) and 'Quiesce'."
105
+ )}
106
+ />
107
+ </label>
108
+ <div className="col-md-8">
109
+ <select
110
+ className="common-select form-control"
111
+ value={snapshotMode}
112
+ onChange={e => setSnapshotMode(e.target.value)}
113
+ >
114
+ {options.map(item => (
115
+ <option key={item.value} value={item.value}>
116
+ {item.name}
117
+ </option>
118
+ ))}
119
+ </select>
120
+ </div>
121
+ </div>
84
122
  </ForemanForm>
85
123
  );
86
124
  };
@@ -93,11 +131,13 @@ SnapshotForm.propTypes = {
93
131
  name: PropTypes.string.isRequired,
94
132
  description: PropTypes.string.isRequired,
95
133
  includeRam: PropTypes.bool.isRequired,
134
+ useQuiesce: PropTypes.bool,
96
135
  }),
97
136
  submitForm: PropTypes.func.isRequired,
98
137
  setModalClosed: PropTypes.func,
99
138
  capabilities: PropTypes.shape({
100
139
  limitSnapshotNameFormat: PropTypes.bool,
140
+ quiesceOption: PropTypes.bool,
101
141
  }),
102
142
  };
103
143
 
@@ -108,10 +148,13 @@ SnapshotForm.defaultProps = {
108
148
  name: '',
109
149
  description: '',
110
150
  includeRam: false,
151
+ useQuiesce: false,
152
+ snapshotMode: '',
111
153
  },
112
154
  setModalClosed: () => {},
113
155
  capabilities: {
114
156
  limitSnapshotNameFormat: false,
157
+ quiesceOption: false,
115
158
  },
116
159
  };
117
160
 
@@ -8,6 +8,8 @@ exports[`SnapshotForm rendering render 1`] = `
8
8
  "description": "",
9
9
  "includeRam": false,
10
10
  "name": "",
11
+ "snapshotMode": "",
12
+ "useQuiesce": false,
11
13
  }
12
14
  }
13
15
  onCancel={[Function]}
@@ -25,6 +27,8 @@ exports[`SnapshotForm rendering render 1`] = `
25
27
  "_exclusive": Object {},
26
28
  "_mutate": undefined,
27
29
  "_nodes": Array [
30
+ "snapshotMode",
31
+ "useQuiesce",
28
32
  "includeRam",
29
33
  "description",
30
34
  "name",
@@ -121,6 +125,56 @@ exports[`SnapshotForm rendering render 1`] = `
121
125
  ],
122
126
  "type": "string",
123
127
  },
128
+ "snapshotMode": StringSchema {
129
+ "_blacklist": RefSet {
130
+ "list": Array [],
131
+ "refs": Array [],
132
+ },
133
+ "_conditions": Array [],
134
+ "_deps": Array [],
135
+ "_exclusive": Object {},
136
+ "_mutate": undefined,
137
+ "_options": Object {
138
+ "abortEarly": true,
139
+ "recursive": true,
140
+ },
141
+ "_type": "string",
142
+ "_typeError": [Function],
143
+ "_whitelist": RefSet {
144
+ "list": Array [],
145
+ "refs": Array [],
146
+ },
147
+ "tests": Array [],
148
+ "transforms": Array [
149
+ [Function],
150
+ ],
151
+ "type": "string",
152
+ },
153
+ "useQuiesce": BooleanSchema {
154
+ "_blacklist": RefSet {
155
+ "list": Array [],
156
+ "refs": Array [],
157
+ },
158
+ "_conditions": Array [],
159
+ "_deps": Array [],
160
+ "_exclusive": Object {},
161
+ "_mutate": undefined,
162
+ "_options": Object {
163
+ "abortEarly": true,
164
+ "recursive": true,
165
+ },
166
+ "_type": "boolean",
167
+ "_typeError": [Function],
168
+ "_whitelist": RefSet {
169
+ "list": Array [],
170
+ "refs": Array [],
171
+ },
172
+ "tests": Array [],
173
+ "transforms": Array [
174
+ [Function],
175
+ ],
176
+ "type": "boolean",
177
+ },
124
178
  },
125
179
  "tests": Array [],
126
180
  "transforms": Array [
@@ -146,14 +200,40 @@ exports[`SnapshotForm rendering render 1`] = `
146
200
  required={false}
147
201
  type="textarea"
148
202
  />
149
- <TextField
150
- className=""
151
- inputClassName="col-md-8"
152
- label="Include RAM"
153
- name="includeRam"
154
- required={false}
155
- type="checkbox"
156
- />
203
+ <div
204
+ className="form-group"
205
+ >
206
+ <label
207
+ className="col-md-2 control-label"
208
+ >
209
+ Snapshot Mode
210
+ <FieldLevelHelp
211
+ buttonClass="field-help"
212
+ content="Select Snapshot Mode between mutually exclusive options, 'Memory' (includes RAM) and 'Quiesce'."
213
+ placement="top"
214
+ rootClose={true}
215
+ />
216
+ </label>
217
+ <div
218
+ className="col-md-8"
219
+ >
220
+ <select
221
+ className="common-select form-control"
222
+ onChange={[Function]}
223
+ >
224
+ <option
225
+ key=""
226
+ value=""
227
+ />
228
+ <option
229
+ key="Memory"
230
+ value="Memory"
231
+ >
232
+ Memory
233
+ </option>
234
+ </select>
235
+ </div>
236
+ </div>
157
237
  </ForemanForm>
158
238
  `;
159
239
 
@@ -165,6 +245,8 @@ exports[`SnapshotForm rendering render with limitSnapshotNameFormat capability 1
165
245
  "description": "",
166
246
  "includeRam": false,
167
247
  "name": "",
248
+ "snapshotMode": "",
249
+ "useQuiesce": false,
168
250
  }
169
251
  }
170
252
  onCancel={[Function]}
@@ -182,6 +264,8 @@ exports[`SnapshotForm rendering render with limitSnapshotNameFormat capability 1
182
264
  "_exclusive": Object {},
183
265
  "_mutate": undefined,
184
266
  "_nodes": Array [
267
+ "snapshotMode",
268
+ "useQuiesce",
185
269
  "includeRam",
186
270
  "description",
187
271
  "name",
@@ -282,6 +366,56 @@ exports[`SnapshotForm rendering render with limitSnapshotNameFormat capability 1
282
366
  ],
283
367
  "type": "string",
284
368
  },
369
+ "snapshotMode": StringSchema {
370
+ "_blacklist": RefSet {
371
+ "list": Array [],
372
+ "refs": Array [],
373
+ },
374
+ "_conditions": Array [],
375
+ "_deps": Array [],
376
+ "_exclusive": Object {},
377
+ "_mutate": undefined,
378
+ "_options": Object {
379
+ "abortEarly": true,
380
+ "recursive": true,
381
+ },
382
+ "_type": "string",
383
+ "_typeError": [Function],
384
+ "_whitelist": RefSet {
385
+ "list": Array [],
386
+ "refs": Array [],
387
+ },
388
+ "tests": Array [],
389
+ "transforms": Array [
390
+ [Function],
391
+ ],
392
+ "type": "string",
393
+ },
394
+ "useQuiesce": BooleanSchema {
395
+ "_blacklist": RefSet {
396
+ "list": Array [],
397
+ "refs": Array [],
398
+ },
399
+ "_conditions": Array [],
400
+ "_deps": Array [],
401
+ "_exclusive": Object {},
402
+ "_mutate": undefined,
403
+ "_options": Object {
404
+ "abortEarly": true,
405
+ "recursive": true,
406
+ },
407
+ "_type": "boolean",
408
+ "_typeError": [Function],
409
+ "_whitelist": RefSet {
410
+ "list": Array [],
411
+ "refs": Array [],
412
+ },
413
+ "tests": Array [],
414
+ "transforms": Array [
415
+ [Function],
416
+ ],
417
+ "type": "boolean",
418
+ },
285
419
  },
286
420
  "tests": Array [],
287
421
  "transforms": Array [
@@ -307,14 +441,40 @@ exports[`SnapshotForm rendering render with limitSnapshotNameFormat capability 1
307
441
  required={false}
308
442
  type="textarea"
309
443
  />
310
- <TextField
311
- className=""
312
- inputClassName="col-md-8"
313
- label="Include RAM"
314
- name="includeRam"
315
- required={false}
316
- type="checkbox"
317
- />
444
+ <div
445
+ className="form-group"
446
+ >
447
+ <label
448
+ className="col-md-2 control-label"
449
+ >
450
+ Snapshot Mode
451
+ <FieldLevelHelp
452
+ buttonClass="field-help"
453
+ content="Select Snapshot Mode between mutually exclusive options, 'Memory' (includes RAM) and 'Quiesce'."
454
+ placement="top"
455
+ rootClose={true}
456
+ />
457
+ </label>
458
+ <div
459
+ className="col-md-8"
460
+ >
461
+ <select
462
+ className="common-select form-control"
463
+ onChange={[Function]}
464
+ >
465
+ <option
466
+ key=""
467
+ value=""
468
+ />
469
+ <option
470
+ key="Memory"
471
+ value="Memory"
472
+ >
473
+ Memory
474
+ </option>
475
+ </select>
476
+ </div>
477
+ </div>
318
478
  </ForemanForm>
319
479
  `;
320
480
 
@@ -343,6 +503,8 @@ exports[`SnapshotForm rendering render with optional Props 1`] = `
343
503
  "_exclusive": Object {},
344
504
  "_mutate": undefined,
345
505
  "_nodes": Array [
506
+ "snapshotMode",
507
+ "useQuiesce",
346
508
  "includeRam",
347
509
  "description",
348
510
  "name",
@@ -439,6 +601,56 @@ exports[`SnapshotForm rendering render with optional Props 1`] = `
439
601
  ],
440
602
  "type": "string",
441
603
  },
604
+ "snapshotMode": StringSchema {
605
+ "_blacklist": RefSet {
606
+ "list": Array [],
607
+ "refs": Array [],
608
+ },
609
+ "_conditions": Array [],
610
+ "_deps": Array [],
611
+ "_exclusive": Object {},
612
+ "_mutate": undefined,
613
+ "_options": Object {
614
+ "abortEarly": true,
615
+ "recursive": true,
616
+ },
617
+ "_type": "string",
618
+ "_typeError": [Function],
619
+ "_whitelist": RefSet {
620
+ "list": Array [],
621
+ "refs": Array [],
622
+ },
623
+ "tests": Array [],
624
+ "transforms": Array [
625
+ [Function],
626
+ ],
627
+ "type": "string",
628
+ },
629
+ "useQuiesce": BooleanSchema {
630
+ "_blacklist": RefSet {
631
+ "list": Array [],
632
+ "refs": Array [],
633
+ },
634
+ "_conditions": Array [],
635
+ "_deps": Array [],
636
+ "_exclusive": Object {},
637
+ "_mutate": undefined,
638
+ "_options": Object {
639
+ "abortEarly": true,
640
+ "recursive": true,
641
+ },
642
+ "_type": "boolean",
643
+ "_typeError": [Function],
644
+ "_whitelist": RefSet {
645
+ "list": Array [],
646
+ "refs": Array [],
647
+ },
648
+ "tests": Array [],
649
+ "transforms": Array [
650
+ [Function],
651
+ ],
652
+ "type": "boolean",
653
+ },
442
654
  },
443
655
  "tests": Array [],
444
656
  "transforms": Array [
@@ -464,13 +676,39 @@ exports[`SnapshotForm rendering render with optional Props 1`] = `
464
676
  required={false}
465
677
  type="textarea"
466
678
  />
467
- <TextField
468
- className=""
469
- inputClassName="col-md-8"
470
- label="Include RAM"
471
- name="includeRam"
472
- required={false}
473
- type="checkbox"
474
- />
679
+ <div
680
+ className="form-group"
681
+ >
682
+ <label
683
+ className="col-md-2 control-label"
684
+ >
685
+ Snapshot Mode
686
+ <FieldLevelHelp
687
+ buttonClass="field-help"
688
+ content="Select Snapshot Mode between mutually exclusive options, 'Memory' (includes RAM) and 'Quiesce'."
689
+ placement="top"
690
+ rootClose={true}
691
+ />
692
+ </label>
693
+ <div
694
+ className="col-md-8"
695
+ >
696
+ <select
697
+ className="common-select form-control"
698
+ onChange={[Function]}
699
+ >
700
+ <option
701
+ key=""
702
+ value=""
703
+ />
704
+ <option
705
+ key="Memory"
706
+ value="Memory"
707
+ >
708
+ Memory
709
+ </option>
710
+ </select>
711
+ </div>
712
+ </div>
475
713
  </ForemanForm>
476
714
  `;
@@ -0,0 +1,61 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+
4
+ import CardTemplate from 'foremanReact/components/HostDetails/Templates/CardItem/CardTemplate';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+
7
+ import SnapshotManagement from '../SnapshotManagement';
8
+
9
+ const SnapshotManagementCard = ({ hostDetails, ...props }) => {
10
+ const children = [];
11
+ const capabilities = {
12
+ editableSnapshotName:
13
+ hostDetails?.capabilities?.includes('editable_snapshot_name') || false,
14
+ limitSnapshotNameFormat:
15
+ hostDetails?.capabilities?.includes('limit_snapshot_name_format') ||
16
+ false,
17
+ quiesceOption:
18
+ hostDetails?.capabilities?.includes('snapshot_include_quiesce') || false,
19
+ };
20
+
21
+ if (hostDetails?.id && hostDetails?.permissions)
22
+ children.push(
23
+ <SnapshotManagement
24
+ key="SnapshotManagement"
25
+ host={{ id: hostDetails.id, name: hostDetails.name }}
26
+ canCreate={hostDetails.permissions.create_snapshots}
27
+ canUpdate={hostDetails.permissions.edit_snapshots}
28
+ canRevert={hostDetails.permissions.revert_snapshots}
29
+ canDelete={hostDetails.permissions.destroy_snapshots}
30
+ capabilities={capabilities}
31
+ />
32
+ );
33
+ return (
34
+ <CardTemplate
35
+ overrideGridProps={{ xl2: 6, xl: 8, lg: 8, md: 12 }}
36
+ header={__('Snapshots')}
37
+ >
38
+ {children}
39
+ </CardTemplate>
40
+ );
41
+ };
42
+
43
+ export default SnapshotManagementCard;
44
+
45
+ SnapshotManagementCard.propTypes = {
46
+ hostDetails: PropTypes.shape({
47
+ name: PropTypes.string,
48
+ id: PropTypes.number,
49
+ capabilities: PropTypes.array,
50
+ permissions: PropTypes.shape({
51
+ create_snapshots: PropTypes.bool,
52
+ edit_snapshots: PropTypes.bool,
53
+ revert_snapshots: PropTypes.bool,
54
+ destroy_snapshots: PropTypes.bool,
55
+ }),
56
+ }),
57
+ };
58
+
59
+ SnapshotManagementCard.defaultProps = {
60
+ hostDetails: {},
61
+ };
@@ -0,0 +1,53 @@
1
+ import { testComponentSnapshotsWithFixtures } from 'react-redux-test-utils';
2
+
3
+ import SnapshotManagementCard from '../SnapshotManagementCard';
4
+
5
+ const hostDetails = {
6
+ id: 42,
7
+ name: 'deep.thought',
8
+ permissions: {
9
+ create_snapshots: true,
10
+ edit_snapshots: false,
11
+ revert_snapshots: true,
12
+ destroy_snapshots: false,
13
+ },
14
+ };
15
+
16
+ const hostDetailsVmware = {
17
+ ...hostDetails,
18
+ capabilities: [
19
+ 'build',
20
+ 'image',
21
+ 'snapshots',
22
+ 'snapshot_include_quiesce',
23
+ 'snapshot_include_ram',
24
+ 'editable_snapshot_name',
25
+ ],
26
+ };
27
+ const hostDetailsProxmox = {
28
+ ...hostDetails,
29
+ capabilities: [
30
+ 'build',
31
+ 'new_volume',
32
+ 'new_interface',
33
+ 'image',
34
+ 'snapshots',
35
+ 'limit_snapshot_name_format',
36
+ ],
37
+ };
38
+
39
+ const fixtures = {
40
+ 'without optional Props': {},
41
+ 'with Props': { hostDetails },
42
+ 'with VMWare capabilities': {
43
+ hostDetails: hostDetailsVmware,
44
+ },
45
+ 'with Proxmox capabilities': {
46
+ hostDetails: hostDetailsProxmox,
47
+ },
48
+ };
49
+
50
+ describe('SnapshotManagementCard', () => {
51
+ describe('renders', () =>
52
+ testComponentSnapshotsWithFixtures(SnapshotManagementCard, fixtures));
53
+ });