@@ -25,12 +25,16 @@ import (
2525 "time"
2626
2727 kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
28+ golibvirt "github.com/digitalocean/go-libvirt"
2829 "k8s.io/apimachinery/pkg/api/meta"
2930 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031 "k8s.io/apimachinery/pkg/runtime"
3132 ctrl "sigs.k8s.io/controller-runtime"
3233 "sigs.k8s.io/controller-runtime/pkg/client"
34+ "sigs.k8s.io/controller-runtime/pkg/event"
35+ "sigs.k8s.io/controller-runtime/pkg/handler"
3336 logger "sigs.k8s.io/controller-runtime/pkg/log"
37+ "sigs.k8s.io/controller-runtime/pkg/source"
3438
3539 "github.com/cobaltcore-dev/kvm-node-agent/internal/certificates"
3640 "github.com/cobaltcore-dev/kvm-node-agent/internal/evacuation"
@@ -48,6 +52,9 @@ type HypervisorReconciler struct {
4852
4953 osDescriptor * systemd.Descriptor
5054 evacuateOnReboot bool
55+
56+ // Channel that can be used to trigger reconcile events.
57+ reconcileCh chan event.GenericEvent
5158}
5259
5360const (
@@ -287,6 +294,47 @@ func (r *HypervisorReconciler) Reconcile(ctx context.Context, req ctrl.Request)
287294 return ctrl.Result {RequeueAfter : 1 * time .Minute }, nil
288295}
289296
297+ // Trigger a reconcile event for the managed hypervisor through the
298+ // event channel which is watched by the controller manager.
299+ func (r * HypervisorReconciler ) triggerReconcile () {
300+ r .reconcileCh <- event.GenericEvent {
301+ Object : & kvmv1.Hypervisor {
302+ TypeMeta : metav1.TypeMeta {
303+ Kind : "Hypervisor" ,
304+ APIVersion : "kvm.cloud.sap/v1" ,
305+ },
306+ ObjectMeta : metav1.ObjectMeta {
307+ Name : sys .Hostname ,
308+ Namespace : sys .Namespace ,
309+ },
310+ },
311+ }
312+ }
313+
314+ // Start is called when the manager starts. It starts the libvirt
315+ // event subscription to receive events when the hypervisor needs to be
316+ // reconciled.
317+ func (r * HypervisorReconciler ) Start (ctx context.Context ) error {
318+ log := logger .FromContext (ctx , "controller" , "hypervisor" )
319+ log .Info ("starting libvirt event subscription" )
320+
321+ // Ensure we're connected to libvirt.
322+ if err := r .Libvirt .Connect (); err != nil {
323+ log .Error (err , "unable to connect to libvirt" )
324+ return err
325+ }
326+
327+ // Domain lifecycle events impact the list of active/inactive domains,
328+ // as well as the allocation of resources on the hypervisor.
329+ r .Libvirt .WatchDomainChanges (
330+ golibvirt .DomainEventIDLifecycle ,
331+ "reconcile-on-domain-lifecycle" ,
332+ func (_ context.Context , _ any ) { r .triggerReconcile () },
333+ )
334+
335+ return nil
336+ }
337+
290338// SetupWithManager sets up the controller with the Manager.
291339func (r * HypervisorReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
292340 ctx := context .Background ()
@@ -296,7 +344,16 @@ func (r *HypervisorReconciler) SetupWithManager(mgr ctrl.Manager) error {
296344 return fmt .Errorf ("unable to get Systemd hostname describe(): %w" , err )
297345 }
298346
347+ // Prepare an event channel that will trigger a reconcile event.
348+ r .reconcileCh = make (chan event.GenericEvent )
349+ src := source .Channel (r .reconcileCh , & handler.EnqueueRequestForObject {})
350+ // Run the Start(ctx context.Context) method when the manager starts.
351+ if err := mgr .Add (r ); err != nil {
352+ return err
353+ }
354+
299355 return ctrl .NewControllerManagedBy (mgr ).
300356 For (& kvmv1.Hypervisor {}).
357+ WatchesRawSource (src ).
301358 Complete (r )
302359}
0 commit comments