in cns/ipampool/metrics/observer.go [85:199]
func (o *observer) observeMetrics(ctx context.Context) error {
// The error group is used to allow individual metrics sources to fail without
// failing out the entire attempt to observe the Pool. This may happen if there is a
// transient issue with the source of the data, or if the source is not available
// (like if the CRD is not installed).
var g errgroup.Group
// Get the current state of world.
var meta metaState
g.Go(func() error {
// Try to fetch the ClusterSubnetState, if available.
if o.cssSrc != nil {
csslist, err := o.cssSrc(ctx)
if err != nil {
return err
}
for i := range csslist {
if csslist[i].Status.Exhausted {
meta.exhausted = true
break
}
}
}
return nil
})
var state ipPoolState
g.Go(func() error {
// Try to fetch the NodeNetworkConfig, if available.
if o.nncSrc != nil {
nnc, err := o.nncSrc(ctx)
if err != nil {
return err
}
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.requestedIPs = nnc.Spec.RequestedIPCount
meta.batch = nnc.Status.Scaler.BatchSize
meta.max = nnc.Status.Scaler.MaxIPCount
}
return nil
})
g.Go(func() error {
// Try to fetch the IPConfigurations, if available.
if o.ipSrc != nil {
ips := o.ipSrc()
state.secondaryIPs = int64(len(ips))
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++
}
}
}
return nil
})
err := g.Wait()
state.currentAvailableIPs = state.secondaryIPs - state.allocatedToPods - state.pendingRelease
state.expectedAvailableIPs = state.requestedIPs - state.allocatedToPods
// Update the metrics.
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))
}
if err != nil {
return errors.Wrap(err, "failed to collect all metrics")
}
return nil
}