Skip to content

Commit b5b5e75

Browse files
authored
Merge pull request #837 from erwindon/minion_select
How to quickly select multiple target minions
2 parents d42feec + c6b452e commit b5b5e75

13 files changed

Lines changed: 276 additions & 42 deletions

File tree

docs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ We suggest to upgrade the SaltStack installation when you are still using a vers
4949
- Keyboard control to apply templates
5050
- Choose between live info and cached info for grains/pillar
5151
- View details of orchestrations and allow to start them
52+
- Act on multiple minions by first selecting them
5253

5354

5455
## Quick start using PAM as authentication method
@@ -400,6 +401,14 @@ Each issue has its own dropdown-menu, which typically contains:
400401
But note that there might be more possible solutions, some of which may actually be more preferred.
401402
* A navigation-command to go to a page for more details.
402403

404+
## Minion selection
405+
Pages that show a simple list of minions allow individual minions to be selected.
406+
Use panel button [] to show an extra column with checkboxes in the table.
407+
Minions can be selected one-by-one or you can use select-all, select-none by clicking on the column header.
408+
The selection can be inverted by using CTRL-click.
409+
The list of selected minions will be used in the command-box and for commands from a panel-menu.
410+
When the column is hidden, the selection-values are just ignored.
411+
403412
## Command documentation
404413

405414
### Internal documentation

saltgui/static/scripts/Character.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export class Character {
2727
Character.WHITE_DOWN_POINTING_TRIANGLE = "\u25BD";
2828
Character.BLACK_DIAMOND = "\u25C6";
2929
Character.BLACK_CIRCLE = "\u25CF";
30+
Character.BALLOT_BOX_UNCHECKED = "\u2610";
31+
Character.BALLOT_BOX_WITH_CHECK = "\u2611";
3032
Character.GEAR = "\u2699";
3133
Character.WARNING_SIGN = "\u26A0" + Character._VARIATION_SELECTOR_16;
3234
Character.HEAVY_CHECK_MARK = "\u2714";

saltgui/static/scripts/CommandBox.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,26 @@ export class CommandBox {
423423
button.disabled = false;
424424
}
425425

426+
static getSelectedMinionList () {
427+
const selectVisible = Utils.getStorageItemBoolean("session", "select_visible", false);
428+
if (!selectVisible) {
429+
return null;
430+
}
431+
432+
// only when the selection is visible
433+
const selectMinions = Utils.getStorageItem("session", "select_minions", "");
434+
const lst = selectMinions.split(",").sort();
435+
while (lst.length > 0 && lst[0] === "") {
436+
lst.shift();
437+
}
438+
// and only when there is a selection
439+
if (lst.length == 0) {
440+
return null;
441+
}
442+
443+
return lst.join(",");
444+
}
445+
426446
static showManualRun (pApi) {
427447
const manualRun = document.getElementById("popup-run-command");
428448
manualRun.style.display = "block";
@@ -477,6 +497,13 @@ export class CommandBox {
477497
CommandBox._populateTemplateCatMenu();
478498
CommandBox._populateTemplateTmplMenu();
479499
CommandBox._populateTestProviders(pApi);
500+
501+
const lst = CommandBox.getSelectedMinionList()
502+
if (lst) {
503+
const targetField = document.getElementById("target");
504+
targetField.value = lst;
505+
TargetType.autoSelectTargetType(lst);
506+
}
480507
}
481508

482509
static _populateTestProviders (pApi) {

saltgui/static/scripts/panels/Beacons.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ export class BeaconsPanel extends Panel {
1010

1111
this.addTitle("Beacons");
1212
this.addSearchButton();
13-
this.addTable(["-menu-", "Minion", "Status", "Beacons"]);
13+
this.addFilterButton();
14+
this.addTable(["-select-", "-menu-", "Minion", "Status", "Beacons"]);
1415
this.setTableSortable("Minion", "asc");
1516
this.setTableClickable("page");
1617
this.addMsg();
@@ -20,6 +21,9 @@ export class BeaconsPanel extends Panel {
2021
const wheelKeyListAllPromise = this.api.getWheelKeyListAll();
2122
const localBeaconsListPromise = this.api.getLocalBeaconsList(null);
2223

24+
const selectVisible = Utils.getStorageItemBoolean("session", "select_visible", false);
25+
this.showSelectColumn(selectVisible);
26+
2327
this.nrMinions = 0;
2428

2529
wheelKeyListAllPromise.then((pWheelKeyListAllData) => {
@@ -104,7 +108,7 @@ export class BeaconsPanel extends Panel {
104108
}
105109

106110
updateOfflineMinion (pMinionId, pMinionsDict) {
107-
super.updateOfflineMinion(pMinionId, pMinionsDict);
111+
super.updateOfflineMinion(pMinionId, pMinionsDict, true);
108112

109113
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
110114

@@ -116,7 +120,7 @@ export class BeaconsPanel extends Panel {
116120

117121
pMinionData = BeaconsPanel.fixBeaconsMinion(pMinionData);
118122

119-
super.updateMinion(null, pMinionId, pAllMinionsGrains);
123+
super.updateMinion(null, pMinionId, pAllMinionsGrains, true);
120124

121125
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
122126

saltgui/static/scripts/panels/Grains.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ export class GrainsPanel extends Panel {
1111

1212
this.addTitle("Grains");
1313
this.addSearchButton();
14+
this.addFilterButton();
1415
this.addHelpButton([
1516
"The content of specific well-known grains can be made visible in",
1617
"columns by configuring their name in the server-side configuration file.",
1718
"See README.md for more details."
1819
]);
1920
this.addWarningField();
20-
this.addTable(["-menu-", "Minion", "Status", "Salt version", "OS version", "Grains"]);
21+
this.addTable(["-select-", "-menu-", "Minion", "Status", "Salt version", "OS version", "Grains"]);
2122

2223
// cannot initialize sorting before all columns are present
2324
// this.setTableSortable("Minion", "asc");
@@ -26,6 +27,9 @@ export class GrainsPanel extends Panel {
2627
}
2728

2829
onShow () {
30+
const selectVisible = Utils.getStorageItemBoolean("session", "select_visible", false);
31+
this.showSelectColumn(selectVisible);
32+
2933
if (this.previewColumsAdded !== true) {
3034
// collect the list of displayed extra grains
3135
this.previewGrains = Utils.getStorageItemList("session", "preview_grains");
@@ -82,7 +86,7 @@ export class GrainsPanel extends Panel {
8286

8387
const minionIds = keys.minions.sort();
8488
for (const minionId of minionIds) {
85-
const minionTr = this.addMinion(minionId, this.previewGrains.length);
89+
const minionTr = this.addMinion(minionId, true, this.previewGrains.length);
8690

8791
// preliminary dropdown menu
8892
this._addMenuItemShowGrains(minionTr.dropdownmenu, minionId);
@@ -101,7 +105,7 @@ export class GrainsPanel extends Panel {
101105
}
102106

103107
updateOfflineMinion (pMinionId, pMinionsDict) {
104-
super.updateOfflineMinion(pMinionId, pMinionsDict);
108+
super.updateOfflineMinion(pMinionId, pMinionsDict, true);
105109

106110
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
107111

@@ -115,7 +119,7 @@ export class GrainsPanel extends Panel {
115119
}
116120

117121
updateMinion (pMinionData, pMinionId, pAllMinionsGrains) {
118-
super.updateMinion(pMinionData, pMinionId, pAllMinionsGrains);
122+
super.updateMinion(pMinionData, pMinionId, pAllMinionsGrains, true);
119123

120124
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
121125

saltgui/static/scripts/panels/HighState.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export class HighStatePanel extends Panel {
2727
this._addMenuItemUseStateHighstate();
2828
this._addMenuItemUseStateApply();
2929
this.addSearchButton();
30+
this.addFilterButton();
3031
this.addPlayPauseButton();
3132
this.addHelpButton([
3233
"This panel shows the latest state.highstate or state.apply job for each minion.",
@@ -35,7 +36,7 @@ export class HighStatePanel extends Panel {
3536
"Click on an individual state to re-apply only that state."
3637
]);
3738
this.addWarningField();
38-
this.addTable(["-menu-", "Minion", "State", "Latest JID", "Target", "Function", "Start Time", "States"]);
39+
this.addTable(["-select-", "-menu-", "Minion", "State", "Latest JID", "Target", "Function", "Start Time", "States"]);
3940
this.setTableSortable("Minion", "asc");
4041
this.setTableClickable("cmd");
4142
this.addMsg();
@@ -48,6 +49,9 @@ export class HighStatePanel extends Panel {
4849
onShow () {
4950
const wheelKeyListAllPromise = this.api.getWheelKeyListAll();
5051

52+
const selectVisible = Utils.getStorageItemBoolean("session", "select_visible", false);
53+
this.showSelectColumn(selectVisible);
54+
5155
this.nrMinions = 0;
5256

5357
const cmdList = [];
@@ -96,14 +100,14 @@ export class HighStatePanel extends Panel {
96100
_addMenuItemStateApply (pMenu, pMinionId) {
97101
pMenu.addMenuItem("Apply state...", () => {
98102
const cmdArr = ["state.apply"];
99-
this.runCommand("", pMinionId, cmdArr);
103+
this.runCommand("", pMinionId, cmdArr, true);
100104
});
101105
}
102106

103107
_addMenuItemStateApplyTest (pMenu, pMinionId) {
104108
pMenu.addMenuItem("Test state...", () => {
105109
const cmdArr = ["state.apply", "test=", true];
106-
this.runCommand("", pMinionId, cmdArr);
110+
this.runCommand("", pMinionId, cmdArr, true);
107111
});
108112
}
109113

@@ -362,7 +366,7 @@ export class HighStatePanel extends Panel {
362366

363367
// we already have the TR
364368
// but this function also clears the row
365-
this.getElement(trId);
369+
this.getElement(trId, true);
366370

367371
// mark the TR as populated
368372
minionTr.jid = pJobId;

saltgui/static/scripts/panels/Keys.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export class KeysPanel extends Panel {
287287
}
288288

289289
_addAcceptedMinion (pMinionId, pMinionsDict) {
290-
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId));
290+
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
291291

292292
const minionIdTd = Utils.createTd();
293293
const minionIdSpan = Utils.createSpan("minion-id", pMinionId);
@@ -309,7 +309,7 @@ export class KeysPanel extends Panel {
309309
}
310310

311311
_addRejectedMinion (pMinionId, pMinionsDict) {
312-
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId));
312+
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
313313

314314
const minionIdTd = Utils.createTd();
315315
const minionIdSpan = Utils.createSpan("minion-id", pMinionId);
@@ -334,7 +334,7 @@ export class KeysPanel extends Panel {
334334
}
335335

336336
_addDeniedMinion (pMinionId, pMinionsDict) {
337-
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId));
337+
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
338338

339339
const minionIdTd = Utils.createTd();
340340
const minionIdSpan = Utils.createSpan("minion-id", pMinionId);
@@ -359,7 +359,7 @@ export class KeysPanel extends Panel {
359359
}
360360

361361
_addPreMinion (pMinionId, pMinionsDict, pInsertAtTop = false) {
362-
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId));
362+
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
363363

364364
const minionIdTd = Utils.createTd();
365365
const minionIdSpan = Utils.createSpan("minion-id", pMinionId);
@@ -392,7 +392,7 @@ export class KeysPanel extends Panel {
392392
}
393393

394394
_addMissingMinion (pMinionId, pMinionsDict) {
395-
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), "UNKNOWN");
395+
const minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
396396

397397
const minionIdTd = Utils.createTd();
398398
const minionIdSpan = Utils.createSpan("minion-id", pMinionId);

saltgui/static/scripts/panels/Login.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,9 @@ export class LoginPanel extends Panel {
495495
const fullReturn = wheelConfigValuesData.saltgui_full_return;
496496
Utils.setStorageItem("session", "full_return", fullReturn);
497497

498+
Utils.setStorageItem("session", "select_visible", "false");
499+
Utils.setStorageItem("session", "select_minions", ",");
500+
498501
const id = wheelConfigValuesData.id;
499502
const clusterId = wheelConfigValuesData.cluster_id;
500503
const clusterPeers = wheelConfigValuesData.cluster_peers;

saltgui/static/scripts/panels/Minions.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ export class MinionsPanel extends Panel {
2121
this._addMenuItemStateApply(this.panelMenu, "*");
2222
this._addMenuItemStateApplyTest(this.panelMenu, "*");
2323
this.addSearchButton();
24+
this.addFilterButton();
2425
this.addWarningField();
25-
this.addTable(["-menu-", "Minion", "Status", "Salt version", "OS version"]);
26+
this.addTable(["-select-", "-menu-", "Minion", "Status", "Salt version", "OS version"]);
2627
this.setTableSortable("Minion", "asc");
2728
this.setTableClickable("cmd");
2829
this.addMsg();
@@ -42,6 +43,9 @@ export class MinionsPanel extends Panel {
4243

4344
const runnerManageVersionsPromise = this.api.getRunnerManageVersions();
4445

46+
const selectVisible = Utils.getStorageItemBoolean("session", "select_visible", false);
47+
this.showSelectColumn(selectVisible);
48+
4549
this.loadMinionsTxt();
4650

4751
wheelKeyListAllPromise.then((pWheelKeyListAllData) => {
@@ -180,7 +184,7 @@ export class MinionsPanel extends Panel {
180184
}
181185

182186
updateOfflineMinion (pMinionId, pMinionsDict) {
183-
super.updateOfflineMinion(pMinionId, pMinionsDict);
187+
super.updateOfflineMinion(pMinionId, pMinionsDict, true);
184188

185189
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
186190

@@ -190,7 +194,7 @@ export class MinionsPanel extends Panel {
190194
}
191195

192196
updateMinion (pMinionData, pMinionId, pAllMinionsGrains) {
193-
super.updateMinion(pMinionData, pMinionId, pAllMinionsGrains);
197+
super.updateMinion(pMinionData, pMinionId, pAllMinionsGrains, true);
194198

195199
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
196200
this._addMenuItemStateApply(minionTr.dropdownmenu, pMinionId);
@@ -210,14 +214,14 @@ export class MinionsPanel extends Panel {
210214
_addMenuItemStateApply (pMenu, pMinionId) {
211215
pMenu.addMenuItem("Apply state...", () => {
212216
const cmdArr = ["state.apply"];
213-
this.runCommand("", pMinionId, cmdArr);
217+
this.runCommand("", pMinionId, cmdArr, true);
214218
});
215219
}
216220

217221
_addMenuItemStateApplyTest (pMenu, pMinionId) {
218222
pMenu.addMenuItem("Test state...", () => {
219223
const cmdArr = ["state.apply", "test=", true];
220-
this.runCommand("", pMinionId, cmdArr);
224+
this.runCommand("", pMinionId, cmdArr, true);
221225
});
222226
}
223227

saltgui/static/scripts/panels/Nodegroups.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class NodegroupsPanel extends Panel {
7979
}
8080

8181
updateOfflineMinion (pMinionId, pMinionsDict) {
82-
super.updateOfflineMinion(pMinionId, pMinionsDict);
82+
super.updateOfflineMinion(pMinionId, pMinionsDict, false);
8383

8484
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
8585

@@ -121,7 +121,7 @@ export class NodegroupsPanel extends Panel {
121121
Utils.addToolTip(minionSpan, "This minion is listed for this nodegroup,\nbut the minion is unknown", "bottom-left");
122122
this.unknown += 1;
123123
}
124-
minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId));
124+
minionTr = this.getElement(Utils.getIdFromMinionId(pMinionId), false);
125125
minionTr.appendChild(minionTd);
126126
minionTr.appendChild(status);
127127
minionTr.appendChild(Utils.createTd());
@@ -346,7 +346,7 @@ export class NodegroupsPanel extends Panel {
346346

347347
const minionIds = keys.minions.sort();
348348
for (const minionId of minionIds) {
349-
const minionTr = this.addMinion(minionId);
349+
const minionTr = this.addMinion(minionId, false);
350350

351351
// preliminary dropdown menu
352352
this._addMenuItemStateApplyMinion(minionTr.dropdownmenu, minionId);
@@ -361,7 +361,7 @@ export class NodegroupsPanel extends Panel {
361361
}
362362

363363
updateMinion (pMinionData, pMinionId, pAllNodegroupsGrains) {
364-
super.updateMinion(pMinionData, pMinionId, pAllNodegroupsGrains);
364+
super.updateMinion(pMinionData, pMinionId, pAllNodegroupsGrains, false);
365365

366366
const minionTr = this.table.querySelector("#" + Utils.getIdFromMinionId(pMinionId));
367367
this._addMenuItemStateApplyMinion(minionTr.dropdownmenu, pMinionId);

0 commit comments

Comments
 (0)