Skip to content

Commit f76c0e9

Browse files
committed
refactor, optimize, don't run API calls in reconciler
1 parent 8ea1032 commit f76c0e9

File tree

9 files changed

+210
-42
lines changed

9 files changed

+210
-42
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ require (
1111
github.com/coreos/go-systemd/v22 v22.5.0
1212
github.com/digitalocean/go-libvirt v0.0.0-20240916165608-bff44a349d9d
1313
github.com/godbus/dbus/v5 v5.1.0
14-
github.com/google/uuid v1.6.0
1514
github.com/onsi/ginkgo/v2 v2.19.0
1615
github.com/onsi/gomega v1.33.1
1716
k8s.io/api v0.31.1
@@ -48,6 +47,7 @@ require (
4847
github.com/google/go-cmp v0.6.0 // indirect
4948
github.com/google/gofuzz v1.2.0 // indirect
5049
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
50+
github.com/google/uuid v1.6.0 // indirect
5151
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
5252
github.com/imdario/mergo v0.3.16 // indirect
5353
github.com/inconshreveable/mousetrap v1.1.0 // indirect

internal/controller/hypervisor_controller.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func (r *HypervisorReconciler) Reconcile(ctx context.Context, req ctrl.Request)
103103
// libvirt
104104
// ====================================================================================================
105105

106-
// Try (re)connect to libvirt
106+
// Try (re)connect to libvirt, update status
107107
if err := r.libvirt.Connect(); err != nil {
108108
log.Error(err, "unable to connect to libvirt system bus")
109109
meta.SetStatusCondition(&hypervisor.Status.Conditions, metav1.Condition{
@@ -121,12 +121,8 @@ func (r *HypervisorReconciler) Reconcile(ctx context.Context, req ctrl.Request)
121121
})
122122

123123
// Update hypervisor instances
124-
hypervisor.Status.Instances, err = r.libvirt.GetInstances()
125-
if err != nil {
126-
log.Error(err, "unable to list instances")
127-
return ctrl.Result{}, err
128-
}
129-
hypervisor.Status.NumInstances = len(hypervisor.Status.Instances)
124+
hypervisor.Status.NumInstances = r.libvirt.GetNumInstances()
125+
hypervisor.Status.Instances, _ = r.libvirt.GetInstances()
130126
}
131127

132128
// ====================================================================================================

internal/controller/hypervisor_controller_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ var _ = Describe("Hypervisor Controller", func() {
105105
GetVersionFunc: func() string {
106106
return "10.9.0"
107107
},
108+
GetNumInstancesFunc: func() int {
109+
return 1
110+
},
108111
},
109112
systemd: &systemd.InterfaceMock{
110113
CloseFunc: func() {},

internal/libvirt/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ type Interface interface {
4343

4444
// GetVersion returns the version of the libvirt daemon.
4545
GetVersion() string
46+
47+
// GetNumInstances returns the number of instances.
48+
GetNumInstances() int
4649
}

internal/libvirt/interface_mock.go

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/libvirt/libvirt.go

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525

2626
"github.com/digitalocean/go-libvirt"
2727
"github.com/digitalocean/go-libvirt/socket/dialers"
28-
"github.com/google/uuid"
2928
"sigs.k8s.io/controller-runtime/pkg/client"
3029
"sigs.k8s.io/controller-runtime/pkg/log"
3130

@@ -38,6 +37,7 @@ type LibVirt struct {
3837
migrationJobs map[string]context.CancelFunc
3938
migrationLock sync.Mutex
4039
version string
40+
domains map[libvirt.ConnectListAllDomainsFlags][]libvirt.Domain
4141
}
4242

4343
func NewLibVirt(k client.Client) *LibVirt {
@@ -52,6 +52,7 @@ func NewLibVirt(k client.Client) *LibVirt {
5252
make(map[string]context.CancelFunc),
5353
sync.Mutex{},
5454
"N/A",
55+
make(map[libvirt.ConnectListAllDomainsFlags][]libvirt.Domain, 2),
5556
}
5657
}
5758

@@ -74,6 +75,10 @@ func (l *LibVirt) Connect() error {
7475
// Run the migration listener in a goroutine
7576
ctx := log.IntoContext(context.Background(), log.Log.WithName("libvirt-migration-listener"))
7677
go l.runMigrationListener(ctx)
78+
79+
// Periodic status thread
80+
ctx = log.IntoContext(context.Background(), log.Log.WithName("libvirt-status-thread"))
81+
go l.runStatusThread(ctx)
7782
}
7883

7984
return err
@@ -92,12 +97,7 @@ func (l *LibVirt) GetInstances() ([]v1alpha1.Instance, error) {
9297

9398
flags := []libvirt.ConnectListAllDomainsFlags{libvirt.ConnectListDomainsActive, libvirt.ConnectListDomainsInactive}
9499
for _, flag := range flags {
95-
domains, _, err := l.virt.ConnectListAllDomains(1, flag)
96-
if err != nil {
97-
return nil, err
98-
}
99-
100-
for _, domain := range domains {
100+
for _, domain := range l.domains[flag] {
101101
instances = append(instances, v1alpha1.Instance{
102102
ID: GetOpenstackUUID(domain),
103103
Name: domain.Name,
@@ -109,36 +109,13 @@ func (l *LibVirt) GetInstances() ([]v1alpha1.Instance, error) {
109109
}
110110

111111
func (l *LibVirt) GetDomainsActive() ([]libvirt.Domain, error) {
112-
domains, _, err := l.virt.ConnectListAllDomains(1, libvirt.ConnectListDomainsActive)
113-
if err != nil {
114-
return nil, err
115-
}
116-
return domains, nil
112+
return l.domains[libvirt.ConnectListDomainsActive], nil
117113
}
118114

119115
func (l *LibVirt) IsConnected() bool {
120116
return l.virt.IsConnected()
121117
}
122118

123-
func GetOpenstackUUID(domain libvirt.Domain) string {
124-
u, err := uuid.FromBytes(domain.UUID[:])
125-
if err != nil {
126-
return ""
127-
}
128-
129-
return u.String()
130-
}
131-
132-
func ByteCountIEC(b uint64) string {
133-
const unit = 1024
134-
if b < unit {
135-
return fmt.Sprintf("%d B", b)
136-
}
137-
div, exp := int64(unit), 0
138-
for n := b / unit; n >= unit; n /= unit {
139-
div *= unit
140-
exp++
141-
}
142-
return fmt.Sprintf("%.1f %ciB",
143-
float64(b)/float64(div), "KMGTPE"[exp])
119+
func (l *LibVirt) GetNumInstances() int {
120+
return len(l.domains[libvirt.ConnectListDomainsActive])
144121
}

internal/libvirt/libvirt_events.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,29 @@ func (l *LibVirt) runMigrationListener(ctx context.Context) {
108108
switch e.Msg.Detail {
109109
case int32(libvirt.DomainEventDefinedAdded):
110110
log.Info("domain added")
111+
// add domain to the list of inactive domains
112+
l.domains[libvirt.ConnectListDomainsInactive] = append(l.domains[libvirt.ConnectListDomainsInactive], domain)
111113
case int32(libvirt.DomainEventDefinedUpdated):
112114
log.Info("domain updated")
113115
case int32(libvirt.DomainEventDefinedRenamed):
114116
log.Info("domain renamed")
115117
case int32(libvirt.DomainEventDefinedFromSnapshot):
116118
log.Info("domain defined from snapshot")
117119
}
120+
case int32(libvirt.DomainEventUndefined):
121+
log.Info("domain undefined")
122+
// remove domain from the list of inactive domains
123+
for i, d := range l.domains[libvirt.ConnectListDomainsInactive] {
124+
if d.Name == domain.Name {
125+
l.domains[libvirt.ConnectListDomainsInactive] = append(
126+
l.domains[libvirt.ConnectListDomainsInactive][:i],
127+
l.domains[libvirt.ConnectListDomainsInactive][i+1:]...)
128+
break
129+
}
130+
}
118131
case int32(libvirt.DomainEventStarted):
132+
// add domain to the list of active domains
133+
l.domains[libvirt.ConnectListDomainsActive] = append(l.domains[libvirt.ConnectListDomainsActive], domain)
119134
switch e.Msg.Detail {
120135
case int32(libvirt.DomainEventStartedBooted):
121136
log.Info("domain booted")
@@ -138,6 +153,16 @@ func (l *LibVirt) runMigrationListener(ctx context.Context) {
138153
}
139154
case int32(libvirt.DomainEventStopped):
140155
log.Info("domain stopped")
156+
157+
// remove domain from the list of active domains
158+
for i, d := range l.domains[libvirt.ConnectListDomainsActive] {
159+
if d.Name == domain.Name {
160+
l.domains[libvirt.ConnectListDomainsActive] = append(
161+
l.domains[libvirt.ConnectListDomainsActive][:i],
162+
l.domains[libvirt.ConnectListDomainsActive][i+1:]...)
163+
break
164+
}
165+
}
141166
l.stopMigrationWatch(ctx, domain)
142167
case int32(libvirt.DomainEventShutdown):
143168
log.Info("domain shutdown")
@@ -146,7 +171,6 @@ func (l *LibVirt) runMigrationListener(ctx context.Context) {
146171
log.Info("domain PM suspended")
147172
case int32(libvirt.DomainEventCrashed):
148173
log.Info("domain crashed")
149-
150174
}
151175

152176
case <-ctx.Done():
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright 2025 SAP SE or an SAP affiliate company and cobaltcore-dev contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, LibVirtVersion 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package libvirt
19+
20+
import (
21+
"context"
22+
"fmt"
23+
"time"
24+
25+
"github.com/digitalocean/go-libvirt"
26+
logger "sigs.k8s.io/controller-runtime/pkg/log"
27+
)
28+
29+
func (l *LibVirt) updateDomains() error {
30+
flags := []libvirt.ConnectListAllDomainsFlags{
31+
libvirt.ConnectListDomainsActive,
32+
libvirt.ConnectListDomainsInactive,
33+
}
34+
35+
// updates all domains (active / inactive)
36+
for _, flag := range flags {
37+
domains, _, err := l.virt.ConnectListAllDomains(1, flag)
38+
if err != nil {
39+
return fmt.Errorf("flag %s: %w", fmt.Sprintf("%T", flag), err)
40+
}
41+
42+
// update the domains
43+
l.domains[flag] = domains
44+
}
45+
return nil
46+
}
47+
48+
func (l *LibVirt) runStatusThread(ctx context.Context) {
49+
log := logger.FromContext(ctx)
50+
log.Info("starting status thread")
51+
52+
// run immediately, and every minute after
53+
_ = l.updateDomains()
54+
55+
for {
56+
select {
57+
case <-time.After(1 * time.Minute):
58+
if err := l.updateDomains(); err != nil {
59+
log.Error(err, "failed to update domains")
60+
}
61+
case <-ctx.Done():
62+
log.Info("shutting down status thread")
63+
return
64+
case <-l.virt.Disconnected():
65+
log.Info("libvirt disconnected, shutting down status thread")
66+
return
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)