From fa69c71c79e1e4cf45ce29a36a861b996de6617b Mon Sep 17 00:00:00 2001 From: Evan Baker Date: Wed, 28 Aug 2024 22:40:05 +0000 Subject: [PATCH] fix: add legacy IPAM metrics back to IPAMv2 Signed-off-by: Evan Baker --- cns/ipampool/{ => metrics}/metrics.go | 59 ++++------ cns/ipampool/metrics/observer.go | 157 ++++++++++++++++++++++++++ cns/ipampool/monitor.go | 28 ++++- cns/ipampool/v2/monitor.go | 45 +++++--- cns/service/main.go | 7 +- crd/clustersubnetstate/client.go | 6 + 6 files changed, 240 insertions(+), 62 deletions(-) rename cns/ipampool/{ => metrics}/metrics.go (65%) create mode 100644 cns/ipampool/metrics/observer.go diff --git a/cns/ipampool/metrics.go b/cns/ipampool/metrics/metrics.go similarity index 65% rename from cns/ipampool/metrics.go rename to cns/ipampool/metrics/metrics.go index 75bcdf806d..c9b30a08f9 100644 --- a/cns/ipampool/metrics.go +++ b/cns/ipampool/metrics/metrics.go @@ -1,4 +1,4 @@ -package ipampool +package metrics import ( "github.com/prometheus/client_golang/prometheus" @@ -6,12 +6,12 @@ import ( ) const ( - subnetLabel = "subnet" - subnetCIDRLabel = "subnet_cidr" - podnetARMIDLabel = "podnet_arm_id" + SubnetLabel = "subnet" + SubnetCIDRLabel = "subnet_cidr" + PodnetARMIDLabel = "podnet_arm_id" customerMetricLabel = "customer_metric" customerMetricLabelValue = "customer metric" - subnetExhaustionStateLabel = "subnet_exhaustion_state" + SubnetExhaustionStateLabel = "subnet_exhaustion_state" SubnetIPExhausted = 1 SubnetIPNotExhausted = 0 ) @@ -23,7 +23,7 @@ var ( Help: "IPs currently in use by Pods on this CNS Node.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamAvailableIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -31,7 +31,7 @@ var ( Help: "IPs available on this CNS Node for use by a Pod.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamBatchSize = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -39,7 +39,7 @@ var ( Help: "IPAM IP pool scaling batch size.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamCurrentAvailableIPcount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -47,7 +47,7 @@ var ( Help: "Current available IP count.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamExpectedAvailableIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -55,7 +55,7 @@ var ( Help: "Expected future available IP count assuming the Requested IP count is honored.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamMaxIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -63,7 +63,7 @@ var ( Help: "Maximum Secondary IPs allowed on this Node.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamPendingProgramIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -71,7 +71,7 @@ var ( Help: "IPs reserved but not yet available (Pending Programming).", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamPendingReleaseIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -79,7 +79,7 @@ var ( Help: "IPs reserved but not available anymore (Pending Release).", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamPrimaryIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -87,7 +87,7 @@ var ( Help: "NC Primary IP count (reserved from Pod Subnet for DNS and IMDS SNAT).", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamRequestedIPConfigCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -95,7 +95,7 @@ var ( Help: "Secondary Pod Subnet IPs requested by this CNS Node (for Pods).", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamSecondaryIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -103,7 +103,7 @@ var ( Help: "Node NC Secondary IP count (reserved usable by Pods).", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamTotalIPCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -111,7 +111,7 @@ var ( Help: "Count of total IP pool size allocated to CNS by DNC.", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamSubnetExhaustionState = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -119,14 +119,14 @@ var ( Help: "IPAM view of subnet exhaustion state", ConstLabels: prometheus.Labels{customerMetricLabel: customerMetricLabelValue}, }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel}, ) IpamSubnetExhaustionCount = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "cx_ipam_subnet_exhaustion_state_count_total", Help: "Count of the number of times the ipam pool monitor sees subnet exhaustion", }, - []string{subnetLabel, subnetCIDRLabel, podnetARMIDLabel, subnetExhaustionStateLabel}, + []string{SubnetLabel, SubnetCIDRLabel, PodnetARMIDLabel, SubnetExhaustionStateLabel}, ) ) @@ -148,24 +148,3 @@ func init() { IpamSubnetExhaustionCount, ) } - -func observeIPPoolState(state ipPoolState, meta metaState) { - labels := []string{meta.subnet, meta.subnetCIDR, meta.subnetARMID} - IpamAllocatedIPCount.WithLabelValues(labels...).Set(float64(state.allocatedToPods)) - IpamAvailableIPCount.WithLabelValues(labels...).Set(float64(state.available)) - IpamBatchSize.WithLabelValues(labels...).Set(float64(meta.batch)) - IpamCurrentAvailableIPcount.WithLabelValues(labels...).Set(float64(state.currentAvailableIPs)) - IpamExpectedAvailableIPCount.WithLabelValues(labels...).Set(float64(state.expectedAvailableIPs)) - IpamMaxIPCount.WithLabelValues(labels...).Set(float64(meta.max)) - IpamPendingProgramIPCount.WithLabelValues(labels...).Set(float64(state.pendingProgramming)) - IpamPendingReleaseIPCount.WithLabelValues(labels...).Set(float64(state.pendingRelease)) - IpamPrimaryIPCount.WithLabelValues(labels...).Set(float64(len(meta.primaryIPAddresses))) - IpamRequestedIPConfigCount.WithLabelValues(labels...).Set(float64(state.requestedIPs)) - IpamSecondaryIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs)) - IpamTotalIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs + int64(len(meta.primaryIPAddresses)))) - if meta.exhausted { - IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(SubnetIPExhausted)) - } else { - IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(SubnetIPNotExhausted)) - } -} diff --git a/cns/ipampool/metrics/observer.go b/cns/ipampool/metrics/observer.go new file mode 100644 index 0000000000..360f3ac2d3 --- /dev/null +++ b/cns/ipampool/metrics/observer.go @@ -0,0 +1,157 @@ +package metrics + +import ( + "context" + "fmt" + "net/netip" + + "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/types" + "github.com/Azure/azure-container-networking/crd/clustersubnetstate/api/v1alpha1" + "github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha" + "github.com/pkg/errors" +) + +// Subnet ARM ID /subscriptions/$(SUB)/resourceGroups/$(GROUP)/providers/Microsoft.Network/virtualNetworks/$(VNET)/subnets/$(SUBNET) +const subnetARMIDTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s" + +// ipPoolState is the current actual state of the CNS IP pool. +type ipPoolState struct { + // allocatedToPods are the IPs CNS gives to Pods. + allocatedToPods int64 + // available are the IPs in state "Available". + available int64 + // currentAvailableIPs are the current available IPs: allocated - assigned - pendingRelease. + currentAvailableIPs int64 + // expectedAvailableIPs are the "future" available IPs, if the requested IP count is honored: requested - assigned. + expectedAvailableIPs int64 + // pendingProgramming are the IPs in state "PendingProgramming". + pendingProgramming int64 + // pendingRelease are the IPs in state "PendingRelease". + pendingRelease int64 + // requestedIPs are the IPs CNS has requested that it be allocated by DNC. + requestedIPs int64 + // secondaryIPs are all the IPs given to CNS by DNC, not including the primary IP of the NC. + secondaryIPs int64 +} + +// metaState is the Monitor's configuration state for the IP pool. +type metaState struct { + batch int64 + exhausted bool + max int64 + primaryIPAddresses map[string]struct{} + subnet string + subnetARMID string + subnetCIDR string +} + +// NewLegacyMetricsObserver creates a closed functional scope which can be invoked to +// observe the legacy IPAM pool metrics. +// +//nolint:lll // ignore line length +func NewLegacyMetricsObserver(ctx context.Context, ipcli func() map[string]cns.IPConfigurationStatus, nnccli func(context.Context) (*v1alpha.NodeNetworkConfig, error), csscli func(context.Context) ([]v1alpha1.ClusterSubnetState, error)) func() error { + return func() error { + return observeMetrics(ctx, ipcli, nnccli, csscli) + } +} + +// generateARMID uses the Subnet ARM ID format to populate the ARM ID with the metadata. +// If either of the metadata attributes are empty, then the ARM ID will be an empty string. +func generateARMID(nc *v1alpha.NetworkContainer) string { + subscription := nc.SubscriptionID + resourceGroup := nc.ResourceGroupID + vnetID := nc.VNETID + subnetID := nc.SubnetID + + if subscription == "" || resourceGroup == "" || vnetID == "" || subnetID == "" { + return "" + } + return fmt.Sprintf(subnetARMIDTemplate, subscription, resourceGroup, vnetID, subnetID) +} + +// observeMetrics observes the IP pool and updates the metrics. Blocking. +// +//nolint:lll // ignore line length +func observeMetrics(ctx context.Context, ipcli func() map[string]cns.IPConfigurationStatus, nnccli func(context.Context) (*v1alpha.NodeNetworkConfig, error), csscli func(context.Context) ([]v1alpha1.ClusterSubnetState, error)) error { + csslist, err := csscli(ctx) + if err != nil { + return err + } + nnc, err := nnccli(ctx) + if err != nil { + return err + } + ips := ipcli() + + var meta metaState + for i := range csslist { + if csslist[i].Status.Exhausted { + meta.exhausted = true + break + } + } + if len(nnc.Status.NetworkContainers) > 0 { + // Set SubnetName, SubnetAddressSpace and Pod Network ARM ID values to the global subnet, subnetCIDR and subnetARM variables. + meta.subnet = nnc.Status.NetworkContainers[0].SubnetName + meta.subnetCIDR = nnc.Status.NetworkContainers[0].SubnetAddressSpace + meta.subnetARMID = generateARMID(&nnc.Status.NetworkContainers[0]) + } + meta.primaryIPAddresses = make(map[string]struct{}) + // Add Primary IP to Map, if not present. + // This is only for Swift i.e. if NC Type is vnet. + for i := 0; i < len(nnc.Status.NetworkContainers); i++ { + nc := nnc.Status.NetworkContainers[i] + if nc.Type == "" || nc.Type == v1alpha.VNET { + meta.primaryIPAddresses[nc.PrimaryIP] = struct{}{} + } + + if nc.Type == v1alpha.VNETBlock { + primaryPrefix, err := netip.ParsePrefix(nc.PrimaryIP) + if err != nil { + return errors.Wrapf(err, "unable to parse ip prefix: %s", nc.PrimaryIP) + } + meta.primaryIPAddresses[primaryPrefix.Addr().String()] = struct{}{} + } + } + + state := ipPoolState{ + secondaryIPs: int64(len(ips)), + requestedIPs: nnc.Spec.RequestedIPCount, + } + for i := range ips { + ip := ips[i] + switch ip.GetState() { + case types.Assigned: + state.allocatedToPods++ + case types.Available: + state.available++ + case types.PendingProgramming: + state.pendingProgramming++ + case types.PendingRelease: + state.pendingRelease++ + } + } + state.currentAvailableIPs = state.secondaryIPs - state.allocatedToPods - state.pendingRelease + state.expectedAvailableIPs = state.requestedIPs - state.allocatedToPods + + labels := []string{meta.subnet, meta.subnetCIDR, meta.subnetARMID} + IpamAllocatedIPCount.WithLabelValues(labels...).Set(float64(state.allocatedToPods)) + IpamAvailableIPCount.WithLabelValues(labels...).Set(float64(state.available)) + IpamBatchSize.WithLabelValues(labels...).Set(float64(meta.batch)) + IpamCurrentAvailableIPcount.WithLabelValues(labels...).Set(float64(state.currentAvailableIPs)) + IpamExpectedAvailableIPCount.WithLabelValues(labels...).Set(float64(state.expectedAvailableIPs)) + IpamMaxIPCount.WithLabelValues(labels...).Set(float64(meta.max)) + IpamPendingProgramIPCount.WithLabelValues(labels...).Set(float64(state.pendingProgramming)) + IpamPendingReleaseIPCount.WithLabelValues(labels...).Set(float64(state.pendingRelease)) + IpamPrimaryIPCount.WithLabelValues(labels...).Set(float64(len(meta.primaryIPAddresses))) + IpamRequestedIPConfigCount.WithLabelValues(labels...).Set(float64(state.requestedIPs)) + IpamSecondaryIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs)) + IpamTotalIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs + int64(len(meta.primaryIPAddresses)))) + if meta.exhausted { + IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(SubnetIPExhausted)) + } else { + IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(SubnetIPNotExhausted)) + } + return nil +} diff --git a/cns/ipampool/monitor.go b/cns/ipampool/monitor.go index babf863964..3227840c06 100644 --- a/cns/ipampool/monitor.go +++ b/cns/ipampool/monitor.go @@ -9,6 +9,7 @@ import ( "time" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/ipampool/metrics" "github.com/Azure/azure-container-networking/cns/logger" "github.com/Azure/azure-container-networking/cns/metric" "github.com/Azure/azure-container-networking/cns/types" @@ -105,9 +106,9 @@ func (pm *Monitor) Start(ctx context.Context) error { case css := <-pm.cssSource: // received an updated ClusterSubnetState pm.metastate.exhausted = css.Status.Exhausted logger.Printf("subnet exhausted status = %t", pm.metastate.exhausted) - IpamSubnetExhaustionCount.With(prometheus.Labels{ - subnetLabel: pm.metastate.subnet, subnetCIDRLabel: pm.metastate.subnetCIDR, - podnetARMIDLabel: pm.metastate.subnetARMID, subnetExhaustionStateLabel: strconv.FormatBool(pm.metastate.exhausted), + metrics.IpamSubnetExhaustionCount.With(prometheus.Labels{ + metrics.SubnetLabel: pm.metastate.subnet, metrics.SubnetCIDRLabel: pm.metastate.subnetCIDR, + metrics.PodnetARMIDLabel: pm.metastate.subnetARMID, metrics.SubnetExhaustionStateLabel: strconv.FormatBool(pm.metastate.exhausted), }).Inc() select { default: @@ -482,6 +483,27 @@ func (pm *Monitor) clampScaler(scaler *v1alpha.Scaler) { } } +func observeIPPoolState(state ipPoolState, meta metaState) { + labels := []string{meta.subnet, meta.subnetCIDR, meta.subnetARMID} + metrics.IpamAllocatedIPCount.WithLabelValues(labels...).Set(float64(state.allocatedToPods)) + metrics.IpamAvailableIPCount.WithLabelValues(labels...).Set(float64(state.available)) + metrics.IpamBatchSize.WithLabelValues(labels...).Set(float64(meta.batch)) + metrics.IpamCurrentAvailableIPcount.WithLabelValues(labels...).Set(float64(state.currentAvailableIPs)) + metrics.IpamExpectedAvailableIPCount.WithLabelValues(labels...).Set(float64(state.expectedAvailableIPs)) + metrics.IpamMaxIPCount.WithLabelValues(labels...).Set(float64(meta.max)) + metrics.IpamPendingProgramIPCount.WithLabelValues(labels...).Set(float64(state.pendingProgramming)) + metrics.IpamPendingReleaseIPCount.WithLabelValues(labels...).Set(float64(state.pendingRelease)) + metrics.IpamPrimaryIPCount.WithLabelValues(labels...).Set(float64(len(meta.primaryIPAddresses))) + metrics.IpamRequestedIPConfigCount.WithLabelValues(labels...).Set(float64(state.requestedIPs)) + metrics.IpamSecondaryIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs)) + metrics.IpamTotalIPCount.WithLabelValues(labels...).Set(float64(state.secondaryIPs + int64(len(meta.primaryIPAddresses)))) + if meta.exhausted { + metrics.IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(metrics.SubnetIPExhausted)) + } else { + metrics.IpamSubnetExhaustionState.WithLabelValues(labels...).Set(float64(metrics.SubnetIPNotExhausted)) + } +} + // CalculateMinFreeIPs calculates the minimum free IP quantity based on the Scaler // in the passed NodeNetworkConfig. // Half of odd batches are rounded up! diff --git a/cns/ipampool/v2/monitor.go b/cns/ipampool/v2/monitor.go index 9f2081d3e7..c097229917 100644 --- a/cns/ipampool/v2/monitor.go +++ b/cns/ipampool/v2/monitor.go @@ -36,28 +36,30 @@ type scaler struct { } type Monitor struct { - z *zap.Logger - scaler scaler - nnccli nodeNetworkConfigSpecUpdater - store ipStateStore - demand int64 - request int64 - demandSource <-chan int - cssSource <-chan v1alpha1.ClusterSubnetState - nncSource <-chan v1alpha.NodeNetworkConfig - started chan interface{} - once sync.Once + z *zap.Logger + scaler scaler + nnccli nodeNetworkConfigSpecUpdater + store ipStateStore + demand int64 + request int64 + demandSource <-chan int + cssSource <-chan v1alpha1.ClusterSubnetState + nncSource <-chan v1alpha.NodeNetworkConfig + started chan interface{} + once sync.Once + legacyMetricsObserver func() error } func NewMonitor(z *zap.Logger, store ipStateStore, nnccli nodeNetworkConfigSpecUpdater, demandSource <-chan int, nncSource <-chan v1alpha.NodeNetworkConfig, cssSource <-chan v1alpha1.ClusterSubnetState) *Monitor { //nolint:lll // it's fine return &Monitor{ - z: z.With(zap.String("component", "ipam-pool-monitor")), - store: store, - nnccli: nnccli, - demandSource: demandSource, - cssSource: cssSource, - nncSource: nncSource, - started: make(chan interface{}), + z: z.With(zap.String("component", "ipam-pool-monitor")), + store: store, + nnccli: nnccli, + demandSource: demandSource, + cssSource: cssSource, + nncSource: nncSource, + started: make(chan interface{}), + legacyMetricsObserver: func() error { return nil }, } } @@ -98,6 +100,9 @@ func (pm *Monitor) Start(ctx context.Context) error { if err := pm.reconcile(ctx); err != nil { pm.z.Error("reconcile failed", zap.Error(err)) } + if err := pm.legacyMetricsObserver(); err != nil { + pm.z.Error("legacy metrics observer failed", zap.Error(err)) + } } } @@ -146,6 +151,10 @@ func (pm *Monitor) buildNNCSpec(request int64) v1alpha.NodeNetworkConfigSpec { return spec } +func (pm *Monitor) WithLegacyMetricsObserver(observer func() error) { + pm.legacyMetricsObserver = observer +} + // calculateTargetIPCountOrMax calculates the target IP count request // using the scaling function and clamps the result at the max IPs. func calculateTargetIPCountOrMax(demand, batch, max int64, buffer float64) int64 { diff --git a/cns/service/main.go b/cns/service/main.go index 81910cb224..3cf94bcc73 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -32,6 +32,7 @@ import ( "github.com/Azure/azure-container-networking/cns/hnsclient" "github.com/Azure/azure-container-networking/cns/imds" "github.com/Azure/azure-container-networking/cns/ipampool" + "github.com/Azure/azure-container-networking/cns/ipampool/metrics" ipampoolv2 "github.com/Azure/azure-container-networking/cns/ipampool/v2" cssctrl "github.com/Azure/azure-container-networking/cns/kubecontroller/clustersubnetstate" mtpncctrl "github.com/Azure/azure-container-networking/cns/kubecontroller/multitenantpodnetworkconfig" @@ -48,6 +49,7 @@ import ( "github.com/Azure/azure-container-networking/cns/wireserver" acn "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/crd" + "github.com/Azure/azure-container-networking/crd/clustersubnetstate" cssv1alpha1 "github.com/Azure/azure-container-networking/crd/clustersubnetstate/api/v1alpha1" "github.com/Azure/azure-container-networking/crd/multitenancy" mtv1alpha1 "github.com/Azure/azure-container-networking/crd/multitenancy/api/v1alpha1" @@ -1366,7 +1368,10 @@ func InitializeCRDState(ctx context.Context, httpRestService cns.HTTPService, cn ipDemandCh := make(chan int) if cnsconfig.EnableIPAMv2 { nncCh := make(chan v1alpha.NodeNetworkConfig) - poolMonitor = ipampoolv2.NewMonitor(z, httpRestServiceImplementation, cachedscopedcli, ipDemandCh, nncCh, cssCh).AsV1(nncCh) + pmv2 := ipampoolv2.NewMonitor(z, httpRestServiceImplementation, cachedscopedcli, ipDemandCh, nncCh, cssCh) + obs := metrics.NewLegacyMetricsObserver(ctx, httpRestService.GetPodIPConfigState, cachedscopedcli.Get, clustersubnetstate.NewClient(manager.GetClient()).List) + pmv2.WithLegacyMetricsObserver(obs) + poolMonitor = pmv2.AsV1(nncCh) } else { poolOpts := ipampool.Options{ RefreshDelay: poolIPAMRefreshRateInMilliseconds * time.Millisecond, diff --git a/crd/clustersubnetstate/client.go b/crd/clustersubnetstate/client.go index 7bbe40d005..d037205ac4 100644 --- a/crd/clustersubnetstate/client.go +++ b/crd/clustersubnetstate/client.go @@ -103,3 +103,9 @@ func (c *Client) Get(ctx context.Context, key types.NamespacedName) (*v1alpha1.C err := c.cli.Get(ctx, key, clusterSubnetState) return clusterSubnetState, errors.Wrapf(err, "failed to get css %v", key) } + +func (c *Client) List(ctx context.Context) ([]v1alpha1.ClusterSubnetState, error) { + clusterSubnetStateList := &v1alpha1.ClusterSubnetStateList{} + err := c.cli.List(ctx, clusterSubnetStateList) + return clusterSubnetStateList.Items, errors.Wrap(err, "failed to list css") +}