Skip to content

Commit 4a10647

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 54c6636 commit 4a10647

File tree

1 file changed

+53
-10
lines changed

1 file changed

+53
-10
lines changed

pkg/pillar/hypervisor/qmp.go

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ func execVNCPassword(socket string, password string) error {
6969
return err
7070
}
7171

72+
// There is errors.Join(), but stupid Yetus has old golang
73+
// and complains with "Join not declared by package errors".
74+
// Use our own.
75+
func joinErrors(err1, err2 error) error {
76+
if err1 == nil {
77+
return err2
78+
}
79+
if err2 == nil {
80+
return err1
81+
}
82+
83+
return fmt.Errorf("%v; %v", err1, err2)
84+
}
85+
7286
func getQemuStatus(socket string) (types.SwState, error) {
7387
// lets parse the status according to
7488
// https://github.com/qemu/qemu/blob/master/qapi/run-state.json#L8
@@ -88,7 +102,21 @@ func getQemuStatus(socket string) (types.SwState, error) {
88102
"preconfig": types.PAUSED,
89103
}
90104

91-
if raw, err := execRawCmd(socket, `{ "execute": "query-status" }`); err == nil {
105+
// We do several retries, because correct QEMU status is very crucial to EVE
106+
// and if for some reason (https://github.com/digitalocean/go-qemu/pull/210)
107+
// the status is unexpected, EVE stops QEMU and game over.
108+
var errs error
109+
state := types.UNKNOWN
110+
for attempt := 1; attempt <= 3; attempt++ {
111+
raw, err := execRawCmd(socket, `{ "execute": "query-status" }`)
112+
if err != nil {
113+
err = fmt.Errorf("[attempt %d] qmp status failed for QMP socket '%s': err: '%v'; (JSON response: '%s')",
114+
attempt, socket, err, raw)
115+
errs = joinErrors(errs, err)
116+
time.Sleep(time.Second)
117+
continue
118+
}
119+
92120
var result struct {
93121
ID string `json:"id"`
94122
Return struct {
@@ -100,18 +128,33 @@ func getQemuStatus(socket string) (types.SwState, error) {
100128
dec := json.NewDecoder(bytes.NewReader(raw))
101129
dec.DisallowUnknownFields()
102130
err = dec.Decode(&result)
103-
var matched bool
104-
var state types.SwState
105131
if err != nil {
106-
err = fmt.Errorf("%v; (JSON received: '%s')", err, raw)
107-
} else if state, matched = qmpStatusMap[result.Return.Status]; !matched {
108-
err = fmt.Errorf("unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')",
109-
result.Return.Status, socket, raw)
132+
err = fmt.Errorf("[attempt %d] failed to parse QMP status response for QMP socket '%s': err: '%v'; (JSON response: '%s')",
133+
attempt, socket, err, raw)
134+
errs = joinErrors(errs, err)
135+
time.Sleep(time.Second)
136+
continue
137+
}
138+
var matched bool
139+
if state, matched = qmpStatusMap[result.Return.Status]; !matched {
140+
err = fmt.Errorf("[attempt %d] unknown QMP status '%s' for QMP socket '%s'; (JSON response: '%s')",
141+
attempt, result.Return.Status, socket, raw)
142+
errs = joinErrors(errs, err)
143+
time.Sleep(time.Second)
144+
continue
110145
}
111-
return state, err
112-
} else {
113-
return types.UNKNOWN, err
146+
147+
if errs != nil {
148+
logrus.Errorf("getQemuStatus: %d retrieving status attempts failed '%v', but eventually '%s' status was retrieved, so return SUCCESS and continue",
149+
attempt, errs, result.Return.Status)
150+
errs = nil
151+
}
152+
153+
// Success
154+
break
114155
}
156+
157+
return state, errs
115158
}
116159

117160
func qmpEventHandler(listenerSocket, executorSocket string) {

0 commit comments

Comments
 (0)