@andrian.yablonskyy/thub-coordinator 1.1.17 → 1.1.18
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.
- package/package.json +2 -2
- package/public/js/client-config.js +10 -1
- package/src/api/agent.js +15 -1
- package/src/api/resource.js +40 -0
- package/src/services/jobs.js +44 -1
- package/src/services/registry.js +15 -1
- package/test/scheduler.test.js +2 -2
- package/test/usb-power.test.js +129 -0
- package/test/views.test.js +25 -1
- package/views/help/_agent-cli.pug +13 -0
- package/views/help/_ci.pug +198 -9
- package/views/help/_client-machines.pug +3 -0
- package/views/help/_client-setup.pug +2 -0
- package/views/help/_docker.pug +29 -0
- package/views/help/_env.pug +3 -0
- package/views/help/_git.pug +24 -11
- package/views/help/_lab-devices.pug +154 -0
- package/views/help/_storage.pug +218 -0
- package/views/help/_troubleshooting.pug +8 -0
- package/views/help/_usb-power.pug +147 -0
- package/views/help/index.pug +7 -1
- package/views/mixins/client-config.pug +3 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
+section('usb-power', 'USB port power (uhubctl)', 'power')
|
|
2
|
+
p.
|
|
3
|
+
An HW Client can switch the power of the USB ports its boards hang off, with
|
|
4
|
+
#[a(href="https://github.com/mvp/uhubctl" target="_blank" rel="noopener") uhubctl]. Use it to power-cycle a
|
|
5
|
+
hung board, start every job from a cold boot, or keep boards off between jobs. It needs a USB hub with
|
|
6
|
+
#[strong per-port power switching] (uhubctl's README lists hubs known to work) and #[code sudo apt install uhubctl]
|
|
7
|
+
on the Client host.
|
|
8
|
+
|
|
9
|
+
h3.h6 Setting it up
|
|
10
|
+
ol.small
|
|
11
|
+
li
|
|
12
|
+
| Find each board's hub and port. Run #[code uhubctl] with no arguments. The hub's #[strong location] is the
|
|
13
|
+
| word after #[code hub], and the #[strong port] is the number after #[code Port]. #[code ppps] means the hub
|
|
14
|
+
| switches each port on its own, and #[code ganged] means it switches all its ports together. A USB3 hub shows up twice
|
|
15
|
+
| (#[code 1-1.4] and #[code 2-1.4] below): use the half the board is on, and uhubctl switches the other with it.
|
|
16
|
+
+code('Client host').
|
|
17
|
+
$ sudo uhubctl
|
|
18
|
+
Current status for hub 1-1.4 [2109:2817 VIA Labs, Inc. USB2.0 Hub, USB 2.10, 4 ports, ppps]
|
|
19
|
+
Port 1: 0100 power
|
|
20
|
+
Port 2: 0103 power enable connect [0483:3748 STMicroelectronics STM32 STLink 066DFF485457725187092834]
|
|
21
|
+
Port 3: 0103 power enable connect [0483:3748 STMicroelectronics STM32 STLink 0670FF505055877267183229]
|
|
22
|
+
Port 4: 0000 off
|
|
23
|
+
Current status for hub 2-1.4 [2109:0817 VIA Labs, Inc. USB3.0 Hub, USB 3.00, 4 ports, ppps]
|
|
24
|
+
Port 1: 02a0 power 5gbps Rx.Detect
|
|
25
|
+
li
|
|
26
|
+
| Try it by hand:
|
|
27
|
+
+code('Client host').
|
|
28
|
+
sudo uhubctl -l 1-1.4 -p 2 -a off # the board on port 2 goes dark
|
|
29
|
+
sudo uhubctl -l 1-1.4 -p 2 -a on
|
|
30
|
+
li
|
|
31
|
+
| List the ports under #[code hw-devices.usbPower.ports] in the Client's config, up to 8. Each port's number is
|
|
32
|
+
| its #[strong position in this list] (from 1), and that's what #[code --port] takes. It isn't the hub's own
|
|
33
|
+
| port number: below, #[code --port 2] is hub #[code 1-1.4] port 3.
|
|
34
|
+
+code('~/.config/thub/dut1.json').
|
|
35
|
+
"hw-devices": {
|
|
36
|
+
"stlinks": [{ "index": 1, "devpath": "1.4.2" }, { "index": 2, "devpath": "1.4.3" }],
|
|
37
|
+
"usbPower": {
|
|
38
|
+
"ports": [
|
|
39
|
+
{ "hub": "1-1.4", "port": 2 },
|
|
40
|
+
{ "hub": "1-1.4", "port": 3 }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
li
|
|
45
|
+
| Restart the Client. It installs a udev rule that lets the #[code plugdev] group (which its service has)
|
|
46
|
+
| switch hub ports without root, and reports the ports to the Coordinator, along with whether uhubctl is
|
|
47
|
+
| installed. Then check:
|
|
48
|
+
+code('Client host').
|
|
49
|
+
sudo systemctl restart thub-client@dut1
|
|
50
|
+
thub-client --config ~/.config/thub/dut1.json power status
|
|
51
|
+
# 1. hub 1-1.4 port 2: on (Port 2: 0103 power enable connect [...])
|
|
52
|
+
# 2. hub 1-1.4 port 3: on (Port 3: 0103 power enable connect [...])
|
|
53
|
+
+note.
|
|
54
|
+
Saving a runner's devices on its #[strong Config] tab keeps #[code usbPower] as it is. The ports are only edited in the
|
|
55
|
+
config file.
|
|
56
|
+
|
|
57
|
+
h3.h6 What the actions do
|
|
58
|
+
p.small.
|
|
59
|
+
Every action switches all the listed ports, unless #[code --port] picks one. The Client runs one power request at a
|
|
60
|
+
time, so a job's own reset, its owner's and one from the bench never overlap.
|
|
61
|
+
.table-responsive
|
|
62
|
+
table.table.table-sm.small.align-middle
|
|
63
|
+
thead
|
|
64
|
+
tr
|
|
65
|
+
th(style="width: 12%") Action
|
|
66
|
+
th What the Client runs
|
|
67
|
+
tbody
|
|
68
|
+
tr
|
|
69
|
+
td: code on
|
|
70
|
+
td: code uhubctl -l 1-1.4 -p 2,3 -a on
|
|
71
|
+
tr
|
|
72
|
+
td: code off
|
|
73
|
+
td: code uhubctl -l 1-1.4 -p 2,3 -a off
|
|
74
|
+
tr
|
|
75
|
+
td: code reset
|
|
76
|
+
td
|
|
77
|
+
| #[code uhubctl … -a off], then a wait of the #[strong reset delay] (1 s unless given, 0–60 s, fractions
|
|
78
|
+
| allowed), then #[code uhubctl … -a on]. Give boards that are slow to discharge a longer delay.
|
|
79
|
+
tr
|
|
80
|
+
td: code status
|
|
81
|
+
td #[code uhubctl -l 1-1.4 -p 2,3], each port's state from it (Client host only)
|
|
82
|
+
|
|
83
|
+
h3.h6 Three ways to switch the power
|
|
84
|
+
.row.g-3.mb-3
|
|
85
|
+
.col-md-4
|
|
86
|
+
.card.h-100
|
|
87
|
+
.card-header.small
|
|
88
|
+
i.bi.bi-pc.me-1
|
|
89
|
+
strong On the Client host
|
|
90
|
+
.card-body.small
|
|
91
|
+
p.
|
|
92
|
+
#[code thub-client power on|off|reset|status [--port N] [--delay SEC]]. It goes through the running Client.
|
|
93
|
+
If a job is running, its log records the action.
|
|
94
|
+
.col-md-4
|
|
95
|
+
.card.h-100
|
|
96
|
+
.card-header.small
|
|
97
|
+
i.bi.bi-play-circle.me-1
|
|
98
|
+
strong At a job's start and end
|
|
99
|
+
.card-body.small
|
|
100
|
+
p.mb-2.
|
|
101
|
+
#[code thub run --power-on-start … --power-on-end … [--power-reset-delay SEC]] (HW jobs). The start action runs
|
|
102
|
+
after the downloads, before the command; if it fails, the job ends #[code ERROR].
|
|
103
|
+
p.mb-0.
|
|
104
|
+
The end action runs however the job ends (failed and canceled jobs too), before the result is reported.
|
|
105
|
+
If it fails, the failure is logged and the verdict stays as it was.
|
|
106
|
+
.col-md-4
|
|
107
|
+
.card.h-100
|
|
108
|
+
.card-header.small
|
|
109
|
+
i.bi.bi-lightning.me-1
|
|
110
|
+
strong While your job runs
|
|
111
|
+
.card-body.small
|
|
112
|
+
p.mb-2.
|
|
113
|
+
#[code thub power on|off|reset <jobId> [--delay SEC] [--port N]]. Power-cycle a hung board straight away,
|
|
114
|
+
without canceling the job.
|
|
115
|
+
p.mb-0.
|
|
116
|
+
Only the job's owner can do this, not even an admin, and only while the job is #[code PREPARING] or
|
|
117
|
+
#[code RUNNING]. It reaches the Client within about a second.
|
|
118
|
+
|
|
119
|
+
+code('Cold-boot the board before the job, leave it off afterwards').
|
|
120
|
+
thub run --type hw --label board:nucleo-f401re \
|
|
121
|
+
--download-file "$IMAGE_URL" \
|
|
122
|
+
--command 'st-flash --reset write "$THUB_DOWNLOAD_1" 0x08000000 && ./ci/test.sh' \
|
|
123
|
+
--power-on-start reset --power-reset-delay 2 \
|
|
124
|
+
--power-on-end off \
|
|
125
|
+
--wait
|
|
126
|
+
+code('The board hung: power-cycle it from another terminal').
|
|
127
|
+
$ thub power reset M-00131
|
|
128
|
+
USB power reset, 1 s off sent to lab-hw-01 for job M-00131 — the job's log shows when it's done.
|
|
129
|
+
$ thub power reset M-00131 --port 2 --delay 3 # just the Client's second port, 3 s off
|
|
130
|
+
$ thub power off M-00131 && thub power on M-00131
|
|
131
|
+
+code('What the job log shows').
|
|
132
|
+
[runner] job start: USB power reset
|
|
133
|
+
[runner] USB power reset: 1-1.4:2, 1-1.4:3 (off 2 s)
|
|
134
|
+
[runner] preparing DUT
|
|
135
|
+
[runner] USB power reset (port 2) requested by alice (thub power)
|
|
136
|
+
[runner] USB power reset: 1-1.4:3 (off 3 s)
|
|
137
|
+
[runner] job end: USB power off
|
|
138
|
+
[runner] USB power off: 1-1.4:2, 1-1.4:3
|
|
139
|
+
+code('From the test script itself, between test cases (the Agent must be installed on the Client host)').
|
|
140
|
+
thub run --type hw --env AGENT_URL --env AGENT_KEY --command './ci/test.sh' --wait
|
|
141
|
+
# in ci/test.sh — the key must be the one that submitted the job:
|
|
142
|
+
thub --url "$AGENT_URL" --key "$AGENT_KEY" power reset "$THUB_JOB_ID" --delay 2
|
|
143
|
+
p.small.mb-0.
|
|
144
|
+
A dry run (#[code --dry-run]) lists the uhubctl calls instead of making them. The command sees what was asked for
|
|
145
|
+
as #[code JOB_POWER_ON_START], #[code JOB_POWER_ON_END] and #[code JOB_POWER_RESET_DELAY]. Problems are covered
|
|
146
|
+
under #[a(href="#troubleshooting") Troubleshooting]. Boards on a smart socket or a PDU outlet are switched by the job
|
|
147
|
+
itself: see #[a(href="#lab-devices") Smart sockets, PDUs and other lab devices].
|
package/views/help/index.pug
CHANGED
|
@@ -12,11 +12,14 @@ block content
|
|
|
12
12
|
{ id: 'agent-setup', label: 'Agent setup' },
|
|
13
13
|
{ id: 'client-setup', label: 'Client setup' },
|
|
14
14
|
{ id: 'client-machines', label: 'Client machines (HW / SW)' },
|
|
15
|
+
{ id: 'usb-power', label: 'USB port power (uhubctl)' },
|
|
16
|
+
{ id: 'lab-devices', label: 'Smart sockets, PDUs, devices' },
|
|
15
17
|
{ id: 'docker', label: 'Using Docker' },
|
|
16
18
|
{ id: 'git', label: 'Using git' },
|
|
17
19
|
{ id: 'agent-cli', label: 'Agent CLI reference' },
|
|
18
20
|
{ id: 'env', label: 'Environment variables' },
|
|
19
|
-
{ id: '
|
|
21
|
+
{ id: 'storage', label: 'Artifact storage & certificates' },
|
|
22
|
+
{ id: 'ci', label: 'CI/CD (GitHub, GitLab, Bitbucket, Jenkins)' },
|
|
20
23
|
{ id: 'troubleshooting', label: 'Troubleshooting' }
|
|
21
24
|
]
|
|
22
25
|
|
|
@@ -58,10 +61,13 @@ block content
|
|
|
58
61
|
include _agent-setup
|
|
59
62
|
include _client-setup
|
|
60
63
|
include _client-machines
|
|
64
|
+
include _usb-power
|
|
65
|
+
include _lab-devices
|
|
61
66
|
include _docker
|
|
62
67
|
include _git
|
|
63
68
|
include _agent-cli
|
|
64
69
|
include _env
|
|
70
|
+
include _storage
|
|
65
71
|
include _ci
|
|
66
72
|
include _troubleshooting
|
|
67
73
|
|
|
@@ -234,10 +234,13 @@ mixin configPanes(r, returnTo, groupsById)
|
|
|
234
234
|
+configPane(p, t[0])
|
|
235
235
|
p.small.text-body-secondary.mb-0 Devices can't be edited here: #{noConfigReport}
|
|
236
236
|
else
|
|
237
|
+
//- What the tabs don't edit (usbPower, README §8.7) goes back as it was.
|
|
238
|
+
- const keep = Object.fromEntries(Object.entries(current).filter(([k]) => !['stlinks', 'uarts', 'usbs'].includes(k)))
|
|
237
239
|
form(
|
|
238
240
|
method="post"
|
|
239
241
|
action=`/runners/${r.id}/config`
|
|
240
242
|
data-client-config="hw"
|
|
243
|
+
data-keep=JSON.stringify(keep)
|
|
241
244
|
data-confirm=`Apply these devices to ${r.name}? The Client writes them to its config file on its next heartbeat and restarts once it has no job running.`
|
|
242
245
|
data-confirm-title="Save Client devices?"
|
|
243
246
|
data-confirm-ok="Save"
|