Skip to content

Commit ab05811

Browse files
committed
pillar/qmp: do several attempts to retrieve valid QEMU status
QEMU status is very crucial to EVE and any error or unexpected status leads to QEMU process is being stopped. There is an issue in the 3rd QMP library: digitalocean/go-qemu#210 And in order to be on a safe side and avoid these kind of problems in the future repeat status qeury several times. Signed-off-by: Roman Penyaev <[email protected]>
1 parent 0638598 commit ab05811

File tree

1 file changed

+52
-10
lines changed

1 file changed

+52
-10
lines changed

pkg/pillar/hypervisor/qmp.go

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,19 @@ func QmpExecDeviceAdd(socket, id string, busnum, devnum uint16) error {
8181
qmpString := fmt.Sprintf(`{ "execute": "device_add", "arguments": { "driver": "usb-host", "hostbus": %d, "hostaddr": %d, "id": "%s"} }`, busnum, devnum, id)
8282
_, err := execRawCmd(socket, qmpString)
8383
return err
84+
85+
// There is errors.Join(), but stupid Yetus has old golang
86+
// and complains with "Join not declared by package errors".
87+
// Use our own.
88+
func joinErrors(err1, err2 error) error {
89+
if err1 == nil {
90+
return err2
91+
}
92+
if err2 == nil {
93+
return err1
94+
}
95+
96+
return fmt.Errorf("%v; %v", err1, err2)
8497
}
8598

8699
func getQemuStatus(socket string) (types.SwState, error) {
@@ -102,7 +115,21 @@ func getQemuStatus(socket string) (types.SwState, error) {
102115
"preconfig": types.PAUSED,
103116
}
104117

105-
if raw, err := execRawCmd(socket, `{ "execute": "query-status" }`); err == nil {
118+
// We do several retries, because correct QEMU status is very crucial to EVE
119+
// and if for some reason (https://github.com/digitalocean/go-qemu/pull/210)
120+
// the status is unexpected, EVE stops QEMU and game over.
121+
var errs error
122+
state := types.UNKNOWN
123+
for attempt := 1; attempt <= 3; attempt++ {
124+
raw, err := execRawCmd(socket, `{ "execute": "query-status" }`)
125+
if err != nil {
126+
err = fmt.Errorf("[attempt %d] qmp status failed for QMP socket '%s': err: '%v'; (JSON response: '%s')",
127+
attempt, socket, err, raw)
128+
errs = joinErrors(errs, err)
129+
time.Sleep(time.Second)
130+
continue
131+
}
132+
106133
var result struct {
107134
ID string `json:"id"`
108135
Return struct {
@@ -114,18 +141,33 @@ func getQemuStatus(socket string) (types.SwState, error) {
114141
dec := json.NewDecoder(bytes.NewReader(raw))
115142
dec.DisallowUnknownFields()
116143
err = dec.Decode(&result)
117-
var matched bool
118-
var state types.SwState
119144
if err != nil {
120-
err = fmt.Errorf("%v; (JSON received: '%s')", err, raw)
121-
} else if state, matched = qmpStatusMap[result.Return.Status]; !matched {
122-
err = fmt.Errorf("unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')",
123-
result.Return.Status, socket, raw)
145+
err = fmt.Errorf("[attempt %d] failed to parse QMP status response for QMP socket '%s': err: '%v'; (JSON response: '%s')",
146+
attempt, socket, err, raw)
147+
errs = joinErrors(errs, err)
148+
time.Sleep(time.Second)
149+
continue
150+
}
151+
var matched bool
152+
if state, matched = qmpStatusMap[result.Return.Status]; !matched {
153+
err = fmt.Errorf("[attempt %d] unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')",
154+
attempt, result.Return.Status, socket, raw)
155+
errs = joinErrors(errs, err)
156+
time.Sleep(time.Second)
157+
continue
124158
}
125-
return state, err
126-
} else {
127-
return types.UNKNOWN, err
159+
160+
if errs != nil {
161+
logrus.Errorf("getQemuStatus: %d retrieving status attempts failed '%v', but eventually '%s' status was retrieved, so return SUCCESS and continue",
162+
attempt, errs, result.Return.Status)
163+
errs = nil
164+
}
165+
166+
// Success
167+
break
128168
}
169+
170+
return state, errs
129171
}
130172

131173
func qmpEventHandler(listenerSocket, executorSocket string) {

0 commit comments

Comments
 (0)