@@ -43,6 +43,7 @@ import (
4343 octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1"
4444 "github.com/openstack-k8s-operators/octavia-operator/pkg/octavia"
4545 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
46+ "k8s.io/apimachinery/pkg/types"
4647 "k8s.io/apimachinery/pkg/util/intstr"
4748
4849 appsv1 "k8s.io/api/apps/v1"
@@ -675,6 +676,109 @@ func (r *OctaviaReconciler) reconcileNormal(ctx context.Context, instance *octav
675676 return ctrl.Result {}, err
676677 }
677678
679+ nodeConfigMap := & corev1.ConfigMap {
680+ ObjectMeta : metav1.ObjectMeta {
681+ Name : octavia .HmConfigMap ,
682+ Namespace : instance .GetNamespace (),
683+ Labels : labels .GetLabels (instance , labels .GetGroupLabel (instance .ObjectMeta .Name ), map [string ]string {}),
684+ },
685+ Data : make (map [string ]string ),
686+ }
687+
688+ // Look for existing config map and if exists, read existing data and match
689+ // against nodes.
690+ foundMap := & corev1.ConfigMap {}
691+ err = helper .GetClient ().Get (ctx , types.NamespacedName {Name : octavia .HmConfigMap , Namespace : instance .GetNamespace ()},
692+ foundMap )
693+ if err != nil {
694+ if k8s_errors .IsNotFound (err ) {
695+ Log .Info (fmt .Sprintf ("Port map %s doesn't exist, creating." , octavia .HmConfigMap ))
696+ } else {
697+ return ctrl.Result {}, err
698+ }
699+ } else {
700+ Log .Info ("Retrieved existing map, updating.." )
701+ nodeConfigMap .Data = foundMap .Data
702+ }
703+
704+ //
705+ // Predictable IPs.
706+ //
707+ // NOTE(beagles): refactoring this might be nice. This could also be
708+ // optimized but the data sets are small (nodes an IP ranges are less than
709+ // 100) so optimization might be a waste.
710+ //
711+ predictableIPParams , err := octavia .GetPredictableIPAM (networkParameters )
712+ if err != nil {
713+ return ctrl.Result {}, err
714+ }
715+ // Get a list of the nodes in the cluster
716+
717+ // TODO(beagles):
718+ // * confirm whether or not this lists only the nodes we want (i.e. ones
719+ // that will host the daemonset)
720+ // * do we want to provide a mechanism to temporarily disabling this list
721+ // for maintenance windows where nodes might be "coming and going"
722+
723+ nodes , _ := helper .GetKClient ().CoreV1 ().Nodes ().List (ctx , metav1.ListOptions {})
724+ updatedMap := make (map [string ]string )
725+ allocatedIPs := make (map [string ]bool )
726+ var predictableIPsRequired []string
727+
728+ // First scan existing allocations so we can keep existing allocations.
729+ // Keeping track of what's required and what already exists. If a node is
730+ // removed from the cluster, it's IPs will not be added to the allocated
731+ // list and are effectively recycled.
732+ for _ , node := range nodes .Items {
733+ Log .Info (fmt .Sprintf ("cluster node name %s" , node .Name ))
734+ portName := fmt .Sprintf ("hm_%s" , node .Name )
735+ if ipValue , ok := nodeConfigMap .Data [portName ]; ok {
736+ updatedMap [portName ] = ipValue
737+ allocatedIPs [ipValue ] = true
738+ Log .Info (fmt .Sprintf ("%s has IP mapping %s: %s" , node .Name , portName , ipValue ))
739+ } else {
740+ predictableIPsRequired = append (predictableIPsRequired , portName )
741+ }
742+ portName = fmt .Sprintf ("rsyslog_%s" , node .Name )
743+ if ipValue , ok := nodeConfigMap .Data [portName ]; ok {
744+ updatedMap [portName ] = ipValue
745+ allocatedIPs [ipValue ] = true
746+ Log .Info (fmt .Sprintf ("%s has IP mapping %s: %s" , node .Name , portName , ipValue ))
747+ } else {
748+ predictableIPsRequired = append (predictableIPsRequired , portName )
749+ }
750+ }
751+ // Get new IPs using the range from predictableIPParmas minus the
752+ // allocatedIPs captured above.
753+ Log .Info (fmt .Sprintf ("Allocating %d predictable IPs" , len (predictableIPsRequired )))
754+ for _ , portName := range predictableIPsRequired {
755+ hmPort , err := octavia .GetNextIP (predictableIPParams , allocatedIPs )
756+ if err != nil {
757+ // An error here is really unexpected- it means either we have
758+ // messed up the allocatedIPs list or the range we are assuming is
759+ // too small for the number of health managers and rsyslog
760+ // containers.
761+ return ctrl.Result {}, err
762+ }
763+ updatedMap [portName ] = hmPort
764+ }
765+
766+ mapLabels := labels .GetLabels (instance , labels .GetGroupLabel (instance .ObjectMeta .Name ), map [string ]string {})
767+ _ , err = controllerutil .CreateOrPatch (ctx , helper .GetClient (), nodeConfigMap , func () error {
768+ nodeConfigMap .Labels = util .MergeStringMaps (nodeConfigMap .Labels , mapLabels )
769+ nodeConfigMap .Data = updatedMap
770+ err := controllerutil .SetControllerReference (instance , nodeConfigMap , helper .GetScheme ())
771+ if err != nil {
772+ return err
773+ }
774+ return nil
775+ })
776+
777+ if err != nil {
778+ Log .Info ("Unable to create config map for health manager ports..." )
779+ return ctrl.Result {}, err
780+ }
781+
678782 octaviaHealthManager , op , err := r .amphoraControllerDaemonSetCreateOrUpdate (instance , networkInfo ,
679783 ampImageOwnerID , instance .Spec .OctaviaHealthManager , octaviav1 .HealthManager )
680784 if err != nil {
0 commit comments