foreman_snapshot_management 2.0.1 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ });